我想知道如何持久化依赖于常规持久属性(例如string,int)的属性以及它们自己的一些自定义转换。
例如,假设我有
class A
{
public int Id {get; set;}
public string SomeString {get; set;}
public object SpecialProperty {get; set;}
}
假设持久化的SpecialProperty需要读取SomeString并依赖于它的值,产生一些byte []然后可以存储在数据库中。
我的第一个想法是使用IUserType,但是这个方法NullSafeGet(IDataReader rs,string [] name,object owner)在SomeString被保持(或不是)之前被调用(或者实际上,在此期间),所以它不是集。
我的第二个想法是使用ICompositeUserType和一些使用包装器的相当复杂的设置。
我的第三个想法是,我可以实现ILifecycle并挂钩OnLoad()方法。但是,如果我想这样做,我需要为byte []有效负载创建一个单独的属性,我真的不想存储它。这当然看起来最容易实现,但也有些不优雅。
e.g。
class A : ILifecycle
{
public int Id {get; set;}
public string SomeString {get; set;}
public object SpecialProperty {get; set;}
private byte[] payloadData { get; set; }
public void OnLoad(ISession s, object id)
{
SpecialProperty = customIn(SomeString, payloadData);
payloadData = null;
}
static object customIn(string a, byte[] payload)
{
// ...
}
}
有人知道一种更简单,更简洁的方法吗?
答案 0 :(得分:0)
我会做以下事情:
class A
{
public virtual int Id {get; set;}
protected virtual string _SomeString { get; set; }
protected virtual object _SpecialProperty { get; set; }
public virtual object SpecialProperty {get { return _SpecialProperty; } }
public virtual string SomeString {get { return _SomeString; } set { _SomeString = value; _SpecialProperty = SomeManipulatorMethod(value); } }
}
我只会映射Id
,_SomeString
和_SpecialProperty
。
答案 1 :(得分:0)
我到目前为止还非常接近。当然,事实证明我们可以从NullSafeGet方法中的IDataReader访问依赖属性。
完整的( - )解决方案在这里:
首先让我们定义一个流畅的映射:
public class AMap : ClassMap<A>
{
public AMap()
{
Id(x => x.Id);
Map(x => x.SomeString);
Map(x => x.SpecialProperty)
.CustomType(typeof (DependentProperty))
;
}
static object DeserialiseSpecialProperty(string SomeString, byte[] specialProperty)
{
// ...
}
static byte[] SerialiseSpecialProperty(object specialProperty)
{
// ...
}
}
现在,实现DependentProperty。
public class DependentProperty: IUserType
{
// change SqlTypes appropriatelly if your backing field is something else
public SqlType[] SqlTypes { get { return new[] {new SqlType(DbType.Binary)}; } }
public Type ReturnedType { get { return typeof (object); } }
public bool IsMutable { get { return false; } }
public int GetHashCode(object x)
{
if (x == null)
return 0;
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var SomeString = (string)rs.GetValue(1);
object obj = NHibernateUtil.Binary.NullSafeGet(rs, names[0]);
return AMap.DeserialiseSpecialProperty(SomeString, (byte[])obj);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
else
((IDataParameter)cmd.Parameters[index]).Value = AMap.DeserialiseSpecialProperty(value);
}
public object DeepCopy(object value) { return value; }
public object Replace(object original, object target, object owner) { return original; }
public object Assemble(object cached, object owner) { return cached; }
public object Disassemble(object value) { return value; }
bool IUserType.Equals(object x, object y) { return object.Equals(x, y); }
}
注意:
IUserType实现基于a nhibernate.info tutorial中的模板。它应该适用于值类型,但如果您正在使用引用类型或深层对象图执行任何复杂操作,则可能必须为各种方法提供不同的实现。
由于直接访问IDataReader,因此非常脆弱。 AMap中映射顺序的更改很可能需要您更新访问的索引。很可能对AMap(甚至可能是A)的其他更改也可能会破坏此实现。
这是令人不快的,是的,但是必须将持久性字段(和自定义序列化器/解串器行李)直接存储在业务对象中,并在每次获取/设置外部属性时调用它们。我认为应该通过持久性验证来获取损坏的索引,这种实现应该可以使业务/持久性问题更加清晰地分离。
将此与(pimp my post?)sqlite's manifest typing capabilities相结合,您可以做一些非常酷的动态打字:)