многие-к-одному, все-удалить-сироту, установить свойство равным нулю, но сущность не удалена - PullRequest
2 голосов
/ 10 марта 2011

Использование NHibernate v3.0. У меня есть класс, похожий на этот:

class Foo
{
  bool barActive;
  Bar bar;
}

Экземпляр Bar полностью управляется из Foo:

  1. когда "barActive" имеет значение true, для "bar" устанавливается экземпляр Bar.
  2. когда для «barActive» установлено значение «false», для поля «bar» устанавливается значение «ноль».

Foo.bar отображается так:

<many-to-one name="bar" column="BarId" cascade="all-delete-orphan" unique="true" />

Однако, когда «bar» установлен в null, он не удаляет запись Bar в базе данных. Bar является унаследованным классом, который также используется в других местах, поэтому я не могу просто сделать это поле компонентом.

Я бы ожидал, что "уникальное" ограничение + "удалить-сирота" справится с этим. Я что-то упустил, или NHibernate не может справиться с этим прозрачно? Если это невозможно, кажется, что мой единственный вариант - вызвать событие, чтобы область более высокого уровня могла вызвать ISession.Delete (bar).

1 Ответ

0 голосов
/ 22 октября 2013

У меня есть обходной путь, который автоматически удалит сироту. Я считаю, что это должно работать для NHibernate версии 3 и выше. Он использует перехватчик - в основном объект, который обрабатывает различные связанные с сеансом события. Когда он обнаруживает операцию обновления на Foo, он добавляет явное удаление для осиротевшего Bar.

using System;
using System.Collections;
using NHibernate;
using NHibernate.Type;

class Interceptor : EmptyInterceptor
{
    private ISession _session;
    private Bar _barOrphan;

    public override void SetSession(ISession session)
    {
        base.SetSession(session);
        _session = session;
    }

    public override bool OnFlushDirty(object entity, object id, object[] currentStates, object[] previousStates, string[] propertyNames, IType[] types)
    {
        if (entity.GetType() != typeof(Foo)) return;

        for (var i = 0; i < propertyNames.Length; i++)
        {
            if (!StringComparer.Ordinal.Equals(propertyNames[i], "bar")) continue;

            object previousState = previousStates[i];
            if (currentStates[i] != previousState)
            {
                _barOrphan = (Bar) previousState;
            }
            break;
        }
    }

    public override void PostFlush(ICollection entities)
    {
        if (_barOrphan == null) return;

        _session.Delete(_barOrphan);
        _barOrphan = null;

        _session.Flush();
    }
}

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

using (ISession session = YourSessionFactoryGoesHere.OpenSession(new Interceptor()))
{
    ...
}

Обратите внимание, что это всего лишь черновик, чтобы объяснить концепцию (надеюсь, я не испортил код, так как переписывал его ;-). В реальном сценарии использования вам, возможно, придется иметь дело с несколькими возможными сиротами, созданными в одной единице работы (событие на одной и той же сущности, например, Foo может иметь bar1 и bar2!), Поэтому вместо одного _barOrphan член, вам понадобится очередь действий по удалению для выполнения в PostFlush(). Вместо жесткого кодирования типов участвующих классов и имени свойства bar, вы захотите использовать обобщенные элементы и селектор свойств (например, PropertySelector.GetPropertyName<Foo>(foo => foo.bar), см. эту ссылку . Ограничение БД может может быть проблема, перемещение операции удаления в Interceptor.PreFlush() может помочь, но я не проверял это. Не забывайте о влиянии на производительность (например, OnFlushDirty() вызывается для каждого обновленного объекта, поэтому не ставьте его в узкое место) ).

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