持久化依赖于其他属性和自定义转换的属性

时间:2009-07-31 16:30:37

标签: nhibernate nhibernate-mapping

我想知道如何持久化依赖于常规持久属性(例如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)
    {
        // ...
    }

}

有人知道一种更简单,更简洁的方法吗?

2 个答案:

答案 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); }
}

注意:

  1. IUserType实现基于a nhibernate.info tutorial中的模板。它应该适用于值类型,但如果您正在使用引用类型或深层对象图执行任何复杂操作,则可能必须为各种方法提供不同的实现。

  2. 由于直接访问IDataReader,因此非常脆弱。 AMap中映射顺序的更改很可能需要您更新访问的索引。很可能对AMap(甚至可能是A)的其他更改也可能会破坏此实现。

    这是令人不快的,是的,但是必须将持久性字段(和自定义序列化器/解串器行李)直接存储在业务对象中,并在每次获取/设置外部属性时调用它们。我认为应该通过持久性验证来获取损坏的索引,这种实现应该可以使业务/持久性问题更加清晰地分离。

  3. 将此与(pimp my post?)sqlite's manifest typing capabilities相结合,您可以做一些非常酷的动态打字:)