NHibernate: использование пользовательского типа в качестве первичного ключа - PullRequest
0 голосов
/ 19 ноября 2010

У меня есть устаревшая схема, которая содержит таблицы с первичными ключами типа binary (16) - это хэш MD5 других столбцов.NHibernate не работает с byte [] в качестве ключа, поскольку он не реализует Equals, поэтому я обернул это в пользовательский тип и предоставил NHibernate реализацию IUserType.Обратите внимание, что MD5Hash является структурой, а не классом.

public struct MD5Hash : IComparable, IComparable<MD5Hash>, IEquatable<MD5Hash> {
    private readonly byte[] contents;
    ...
}

Все работало нормально, пока я не создал отображение типа "многие-к-одному" на тип, использующий в качестве ключа MD5Hash.

public class Referenced : IEquatable<Referenced> {
    ...
    public virtual MD5Hash Id { get; set; }
    public virtual string Name { get; set; } // must NOT be null
    ...
}

public class Referencer : IEquatable<Referencer> {
    ...
    public virtual MD5Hash Id { get; set; }
    public virtual Referenced Other { get; set } // may be null
    ...
}

Когда я пытаюсь загрузить объекты типа Referencer, NHibernate не видит нулевое значение для ключа, когда строка содержит значение NULL, поэтому он пытается создать экземпляр объекта типа Referenced, назначить его для Referencer и обновить Referencer вбаза данных.Поскольку у Referenced есть свойство Name, которое отображается в необнуляемый столбец, NHibernate вызывает исключение.Я хочу, чтобы NHibernate установил для свойства Other значение null.

Я мог бы изменить определение MD5Hash на класс вместо структуры, но у меня есть неизвестное количество мест в коде, которое, вероятно, предполагает MD5Hashникогда не может быть нулевым, поэтому я ищу другое решение.

Код для пользовательского типа ...

internal class MD5HashType : IUserType {
    public SqlType[] SqlTypes {
        get { return new[] { new SqlType(DbType.Binary, 16) }; }
    }

    public Type ReturnedType {
        get { return typeof(MD5Hash); }
    }

    public new bool Equals(object x, object y) {
        return Object.Equals(x, y);
    }

    public int GetHashCode(object x) {
        return (null == x) ? 0 : x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner) {
        var val = NHibernateUtil.Binary.NullSafeGet(rs, names[0]);
        return (null == val || DBNull.Value == val) ? MD5Hash.Empty : new MD5Hash((byte[])val);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index) {
        var val = (MD5Hash.Empty == ((MD5Hash)value)) ? null : ((MD5Hash)value).ToByteArray();
        NHibernateUtil.Binary.NullSafeSet(cmd, val, index);
    }

    public object DeepCopy(object value) {
        return value;
    }

    public bool IsMutable {
        get { return false; }
    }

    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;
    }
}

1 Ответ

0 голосов
/ 23 ноября 2010

Проблема заключается в том, что NHibernate не может сказать, что MD5Hash.Empty означает отсутствие значения. Вы пытались создать собственные обработчики событий, такие как следующие, чтобы справиться с этим? Что-то вроде:

public class CustomLoadListener : DefaultLoadEventListener {
    public override void OnLoad(LoadEvent @event, LoadType loadType) {
        if(@event.EntityId is MD5Hash) {
            var id = (MD5Hash) @event.EntityId;
            if(id == MD5Hash.Empty) {
                @event.Result = new Referenced { Id = MD5Hash.Empty };
                return;
            }
        }
        base.OnLoad(@event, loadType);
    }
}

public class CustomSaveOrUpdateListener : DefaultSaveOrUpdateEventListener {
    public override void OnSaveOrUpdate(SaveOrUpdateEvent @event) {
        var entity = @event.Entity as Referenced;
        if(entity != null && entity.Id == MD5Hash.Empty) {
            return;
        }
        base.OnSaveOrUpdate(@event);
    }
}

Затем вам нужно будет настроить этих слушателей на фабрике сеансов через hibernate.cfg.xml:

<session-factory>
    <!-- various properties -->
    <listener type="load" class="NhHacking.CustomLoadListener, NhHacking"/>
    <listener type="save-update" class="NhHacking.CustomSaveOrUpdateListener, NhHacking"/>
</session-factory>

Если у кого-то есть лучшее представление о том, как этого добиться, я бы хотел это услышать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...