NHibernate много-к-одному отображению: если parent равен нулю, установите внешний ключ как пустой Guid вместо нуля - PullRequest
2 голосов
/ 01 марта 2011

То, что я пытаюсь сделать, на самом деле довольно прямолинейно, но, похоже, я не могу правильно составить карту с помощью NHibernate.

Я работаю с базой данных с родительскими и дочерними объектами. Дочерние объекты имеют ссылку внешнего ключа на первичный ключ родительского объекта, имеющий тип данных Guid. Довольно нормально в любом случае. Теперь база данных настроена таким образом, что поле внешнего ключа никогда не должно быть нулевым, поэтому в случае объектов-сирот без родительского элемента внешний ключ должен быть пустым Guid ('00000000-0000-0000-0000-000000000000 «).

То, как я настраивал Nhibernate, долгое время работало нормально, но недавно я сделал отношения двунаправленными, и тогда начались проблемы. Очевидно, что NHibernate увидит, что родительский объект является нулевым, и попытается сохранить нулевое значение в поле внешнего ключа, но это недопустимо!

Пример структуры отображения отношений, которую я использую, приведен ниже.

Отображение родительской стороны:

<id name="ID" column="ID">
    <generator class="guid" />
</id>
<bag name="Children" table="Children" lazy="false" cascade="all" inverse="true">
  <key column="FK_OwnerID" not-null="true"/>
  <one-to-many class="Childclass"/>
</bag>

Отображение на стороне ребенка:

<many-to-one name="Owner" column="FK_OwnerID" not-found="ignore" not-null="false" class="OwnerClass"/>

Я пробовал с разными свойствами, но безрезультатно. Я вынужден использовать свойства insert = "false" и update = "false", и если да, то как именно поддерживать отношения?

Заранее спасибо за любую помощь.

1 Ответ

2 голосов
/ 01 марта 2011

Если я правильно понимаю, ваша структура БД не обеспечивает ссылочную целостность (FK_OwnerID на самом деле не является внешним ключом), но через ваши изменения в отображении вы говорите NHibernate принудительно применить это.Я думаю, что это не может работать.

В принципе, я вижу два варианта решения этой проблемы:

  1. Создание фиктивной записи с вашим 000-Guid в вашей БД, которая выступает в качестве родителя длявсе эти сироты.
  2. Измените вашу таблицу так, чтобы в этом столбце было разрешено NULL, и установите для нее реальное ограничение внешнего ключа (с помощью "on cascade set null).1010 * Редактировать: Если опция 1 не является опцией, я предлагаю вариант 3. Вы можете попробовать функцию перехватчика NHibernate. Вам необходимо реализовать IInterceptor (или наследовать от EmptyInterceptor). Метод OnSave () используется дляДля новых объектов OnFlushDirty используется для измененных объектов. Здесь мы создаем новый «виртуальный» объект с требуемым идентификатором и присваиваем ему.
    using System;
    using NHibernate;
    
    namespace NameSpaceWithDAL
    {
        public class TestInterceptor : NHibernate.EmptyInterceptor
        {
            public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
            {
                if (entity is ChildClass && (entity as ChildClass).Owner == null)
                {
                    SetState(propertyNames, state, "Owner", new OwnerClass { ID = "000..." });
                }
    
                return true;
            }    
    
            public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
            {
                if (entity is ChildClass && (entity as ChildClass).Owner == null)
                {
                    SetState(propertyNames, state, "Owner", new OwnerClass { ID = "000..." });
                }
    
                return true;
            }
    
            private void SetState(string[] propertyNames, object[] state, string propertyname, object value)
            {
                var index = Array.IndexOf(propertyNames, propertyname);
                if (index == -1) return;
                state[index] = value;
            }    
        }
    }
    

    Для использования этого перехватчику необходимо:быть определенным для сеанса (в OpenSession ()) или в общей конфигурации:

    new Configuration().SetInterceptor(new TestInterceptor());
    

    Я тестировал приведенный выше код с простыми свойствами, поэтому не могу сказать, действительно ли этоКс с отношениями.

    Примеры кода взяты из Mike O'Brien

...