сохранение свойства, которое зависит от других свойств и пользовательских преобразований - PullRequest
0 голосов
/ 31 июля 2009

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

}

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

Ответы [ 2 ]

0 голосов
/ 30 ноября 2009

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

Полное (-ише) решение здесь:

Сначала давайте определим беглое отображение:

    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 основана на шаблоне из учебника nhibernate.info . Это должно подходить для типов значений, но если вы делаете что-то сложное с ссылочными типами или графами глубоких объектов, вам может потребоваться предоставить различные реализации для различных методов.

  2. Из-за прямого доступа к IDataReader это ОЧЕНЬ БРИТЛЫ . Изменения порядка отображений в AMap, скорее всего, потребуют от вас обновления доступного индекса. Вполне возможно, что другие изменения в AMap (и, возможно, даже A) также могут нарушить эту реализацию.

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

  3. Объедините это с (pimp my post?) Возможности печатания sqlite , и вы сможете делать очень крутые вещи динамической печати:)

0 голосов
/ 31 июля 2009

Я бы сделал что-то вроде следующего:

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.

...