2009-07-31 6 views
0

Мне интересно, как сохранить свойство, которое зависит как от постоянных сохраняемых свойств (например, строки, 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 [], владелец объекта) вызывается до (или фактически, во время) 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) 
    { 
     // ... 
    } 

} 

Кто-нибудь знает о более легком и, возможно, более сжатом способе?

ответ

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.

0

Я был так далеко, и все же так близко. Конечно, оказывается, что мы можем получить доступ к зависимому свойству из IDataReader в методе NullSafeGet.

Комплексное решение (-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 это VERY BRITTLE. Изменения порядка сопоставлений в AMap, скорее всего, потребуют обновления доступного индекса. Вполне возможно, что другие изменения в AMap (и, возможно, даже A) могут также нарушить эту реализацию.

    Это неприятно, да, но тогда необходимо хранить поля персистентности (и пользовательский сериализатор/десериализационный багаж) непосредственно в ваших бизнес-объектах и ​​вызывать их каждый раз, когда внешнее свойство get/set'd. Я хотел бы утверждать, что разбитый индекс должен быть подхвачен вашей валидацией настойчивости, и эта реализация должна привести к более чистому разделению проблем бизнеса/персистентности.

  3. Объедините это с (сутенер мой пост?) sqlite's manifest typing capabilities и вы можете сделать некоторые очень крутой динамической типизации вещи :)