【问题标题】:persisting a property that relies on other properties and custom transforms持久化依赖于其他属性和自定义转换的属性
【发布时间】:2010-11-15 20:39:03
【问题描述】:

我想知道如何持久化依赖于常规持久化属性(例如字符串、int)以及它们自己的一些自定义转换的属性。

例如假设我有

class A
{
    public int Id {get; set;}
    public string SomeString {get; set;}
    public object SpecialProperty {get; set;}
}

假设持久化 SpecialProperty 需要读取 SomeString 并根据它的值生成一些字节[],然后可以将其存储在数据库中。

我的第一个想法是使用 IUserType,但是这个方法 NullSafeGet(IDataReader rs, string[] names, object owner) 在 SomeString 被持久化(或不持久化)之前(或实际上,期间)被调用,所以它不是设置。

我的第二个想法是使用 ICompositeUserType 和一些使用包装器的相当复杂的设置。

我的第三个想法是也许我可以实现 ILifecycle 并挂钩 OnLoad() 方法。但是,如果我想这样做,我需要为我不想存储的 byte[] 有效负载创建一个单独的属性。这当然看起来最容易实现,但也有些不雅。

例如

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)
    {
        // ...
    }

}

有没有人知道一种更简单且可能更简洁的方法?

【问题讨论】:

    标签: nhibernate nhibernate-mapping


    【解决方案1】:

    我走得那么远,却又那么近。当然,事实证明我们可以从 NullSafeGet 方法中的 IDataReader 访问依赖属性。

    一个完整的(-ish)解决方案在这里:

    首先让我们定义一个流畅的映射:

        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. 将它与(皮条客我的帖子?)sqlite's manifest typing capabilities 结合起来,你可以做一些非常酷的动态打字工作:)

    【讨论】:

      【解决方案2】:

      我会做如下的事情:

      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

      【讨论】:

        猜你喜欢
        • 2011-09-09
        • 1970-01-01
        • 2017-12-16
        • 2016-07-08
        • 1970-01-01
        • 2019-12-17
        • 2017-02-08
        • 1970-01-01
        • 2011-08-20
        相关资源
        最近更新 更多