У меня есть обходной путь, который автоматически удалит сироту. Я считаю, что это должно работать для 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()
вызывается для каждого обновленного объекта, поэтому не ставьте его в узкое место) ).