NHibernate ICompositeUserType 拆分字符串列
NHibernate ICompositeUserType split string columns
我将为字符串 属性 实现一个 ICompositeUserType,它被分成 4 列,每列 2000 个字符(在旧的 oracle 数据库中,以避免 CLOB/LONG 字段)。
实现接口似乎是个好主意,但我无法理解 SetPropertyValue 方法。它不接受类型化或字符串值。我需要它是可变的,所以我需要实现这个方法。
我可以有一个非持久性 属性 来封装拆分和连接到 x 个持久性属性,但我想要更透明的东西。
我发现了类似的问题,但 none 的回复符合我的预期。
-- 更新--
我上传了一些代码来为问题添加上下文:
数据库:
...
Value VARCHAR2(2000 BYTE)
Value2 VARCHAR2(2000 BYTE)
Value3 VARCHAR2(2000 BYTE)
Value4 VARCHAR2(2000 BYTE)
Value5 VARCHAR2(2000 BYTE)
...
HBM:
<property name="Value" type="MyAssembly.NHibernateUtils.Types.MultiColumnJoinedUserType, MyAssembly">
<column name="DFM_Value"/>
<column name="DFM_Value2"/>
<column name="DFM_Value3"/>
<column name="DFM_Value4"/>
<column name="DFM_Value5"/>
</property>
CLASS:
public virtual string Value { get; set; }
MultiColumnJoinedUserType:
/// <summary>
/// Set the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"></param>
/// <param name="value">the value to set</param>
public void SetPropertyValue(object component, int property, object value) { ... }
在我的 class 中,方法的组件字段是一个字符串,因此在不更改引用的情况下不可能更改其值,因为字符串作为值而不是我需要的引用传递。
ICompositeUserType
通常用于您在 .Net 中拥有多个属性的情况(例如 "component")。这里有一个 Money
class 的例子:http://geekswithblogs.net/opiesblog/archive/2006/08/05/87218.aspx
由于您在 .Net 中只有一个简单的值,您应该改为实现 IUserType
,这仍然允许您在数据库中使用多个列。
我在这个问题上花了几个小时,尝试了各种解决方案:
- 在 ICompositeUserType 中使用字符串[] 而不是字符串
- 使用 IUserType 而不是 ICompositeUserType
两者都在 Oracle 中工作,但在 SQL(我需要兼容性)中,空值被设置为空值的默认值。
log4net 中的 NHibernate 跟踪:
INSERT INTO VALUETABLE (HDF_Value1, HDF_Value2, HDF_Value3, HDF_Value4, HDF_Value5) VALUES (@p1, @p2, @p3, @p4, @p5); select SCOPE_IDENTITY(); @p1 = NULL [Type: String (4000)], @p2 = NULL [Type: String (4000)], @p3 = NULL [Type: String (4000)], @p4 = NULL [Type: String (4000)], @p5 = NULL [Type: String (4000)]
但是在 SQLProfiler 中:
exec sp_executesql N'INSERT INTO VALUETABLE (HDF_Value1, HDF_Value2, HDF_Value3, HDF_Value4, HDF_Value5) VALUES (@p1, @p2, @p3, @p4, @p5); select SCOPE_IDENTITY()',N'@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 nvarchar(4000),@p4 nvarchar(4000),@p5 nvarchar(4000)',@p1=default,@p2=default,@p3=default,@p4=default,@p5=default
我在 nullsafeset 上尝试了各种代码(使用 DBNull、string.NullSafeSet、...),但对我没有任何作用。我不能浪费更多的时间,最后我实现了最简单的解决方案,映射对象上的 5 个 "hidden" 属性和一个拆分和连接值的 "virtual" 属性,它不是非常透明,但按预期工作:
public virtual string _Value1 { get; set; }
public virtual string _Value2 { get; set; }
public virtual string _Value3 { get; set; }
public virtual string _Value4 { get; set; }
public virtual string _Value5 { get; set; }
public virtual string Value
{
get
{
// JOIN VALUES FROM 5 _Value properties
}
set
{
// SPLIT VALUE INTO 5 _Value properties
}
}
这一刻对我来说已经足够了。
字符串不可变。您可以安全地忽略 SetPropertyValue
。我已经在我的不可变用户类型中实现了 GetPropertyValue
,我认为它被调用了。
public bool IsMutable
{
get { return false; }
}
public void SetPropertyValue(object component, int property, object value)
{
throw new NotSupportedException("Something goes wrong here");
}
public object GetPropertyValue(object component, int property)
{
var stringComponent = (string)component;
maxLengthToRead = Math.Min(2000, stringComponent.Length - property * 2000);
if (maxLengthToRead <= 0) return string.Empty;
return stringComponent.Substring(property * 2000, maxLengthToRead);
}
我将为字符串 属性 实现一个 ICompositeUserType,它被分成 4 列,每列 2000 个字符(在旧的 oracle 数据库中,以避免 CLOB/LONG 字段)。 实现接口似乎是个好主意,但我无法理解 SetPropertyValue 方法。它不接受类型化或字符串值。我需要它是可变的,所以我需要实现这个方法。
我可以有一个非持久性 属性 来封装拆分和连接到 x 个持久性属性,但我想要更透明的东西。
我发现了类似的问题,但 none 的回复符合我的预期。
-- 更新--
我上传了一些代码来为问题添加上下文:
数据库:
...
Value VARCHAR2(2000 BYTE)
Value2 VARCHAR2(2000 BYTE)
Value3 VARCHAR2(2000 BYTE)
Value4 VARCHAR2(2000 BYTE)
Value5 VARCHAR2(2000 BYTE)
...
HBM:
<property name="Value" type="MyAssembly.NHibernateUtils.Types.MultiColumnJoinedUserType, MyAssembly">
<column name="DFM_Value"/>
<column name="DFM_Value2"/>
<column name="DFM_Value3"/>
<column name="DFM_Value4"/>
<column name="DFM_Value5"/>
</property>
CLASS:
public virtual string Value { get; set; }
MultiColumnJoinedUserType:
/// <summary>
/// Set the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"></param>
/// <param name="value">the value to set</param>
public void SetPropertyValue(object component, int property, object value) { ... }
在我的 class 中,方法的组件字段是一个字符串,因此在不更改引用的情况下不可能更改其值,因为字符串作为值而不是我需要的引用传递。
ICompositeUserType
通常用于您在 .Net 中拥有多个属性的情况(例如 "component")。这里有一个 Money
class 的例子:http://geekswithblogs.net/opiesblog/archive/2006/08/05/87218.aspx
由于您在 .Net 中只有一个简单的值,您应该改为实现 IUserType
,这仍然允许您在数据库中使用多个列。
我在这个问题上花了几个小时,尝试了各种解决方案:
- 在 ICompositeUserType 中使用字符串[] 而不是字符串
- 使用 IUserType 而不是 ICompositeUserType
两者都在 Oracle 中工作,但在 SQL(我需要兼容性)中,空值被设置为空值的默认值。
log4net 中的 NHibernate 跟踪:
INSERT INTO VALUETABLE (HDF_Value1, HDF_Value2, HDF_Value3, HDF_Value4, HDF_Value5) VALUES (@p1, @p2, @p3, @p4, @p5); select SCOPE_IDENTITY(); @p1 = NULL [Type: String (4000)], @p2 = NULL [Type: String (4000)], @p3 = NULL [Type: String (4000)], @p4 = NULL [Type: String (4000)], @p5 = NULL [Type: String (4000)]
但是在 SQLProfiler 中:
exec sp_executesql N'INSERT INTO VALUETABLE (HDF_Value1, HDF_Value2, HDF_Value3, HDF_Value4, HDF_Value5) VALUES (@p1, @p2, @p3, @p4, @p5); select SCOPE_IDENTITY()',N'@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 nvarchar(4000),@p4 nvarchar(4000),@p5 nvarchar(4000)',@p1=default,@p2=default,@p3=default,@p4=default,@p5=default
我在 nullsafeset 上尝试了各种代码(使用 DBNull、string.NullSafeSet、...),但对我没有任何作用。我不能浪费更多的时间,最后我实现了最简单的解决方案,映射对象上的 5 个 "hidden" 属性和一个拆分和连接值的 "virtual" 属性,它不是非常透明,但按预期工作:
public virtual string _Value1 { get; set; }
public virtual string _Value2 { get; set; }
public virtual string _Value3 { get; set; }
public virtual string _Value4 { get; set; }
public virtual string _Value5 { get; set; }
public virtual string Value
{
get
{
// JOIN VALUES FROM 5 _Value properties
}
set
{
// SPLIT VALUE INTO 5 _Value properties
}
}
这一刻对我来说已经足够了。
字符串不可变。您可以安全地忽略 SetPropertyValue
。我已经在我的不可变用户类型中实现了 GetPropertyValue
,我认为它被调用了。
public bool IsMutable
{
get { return false; }
}
public void SetPropertyValue(object component, int property, object value)
{
throw new NotSupportedException("Something goes wrong here");
}
public object GetPropertyValue(object component, int property)
{
var stringComponent = (string)component;
maxLengthToRead = Math.Min(2000, stringComponent.Length - property * 2000);
if (maxLengthToRead <= 0) return string.Empty;
return stringComponent.Substring(property * 2000, maxLengthToRead);
}