Я подключил IPreInsertEventListener
и IPreUpdateEventListener
для управления журналом аудита в моем приложении, которое использует NHibernate в качестве слоя ORM.
Проблема, с которой я столкнулся, заключается в том, что событие OnPreInsert()
вызывается, когда объект сохраняется, но не также для каждого вставленного дочернего объекта.
В моем случае у меня есть Canine
, у которого есть коллекция CanineHandlers
. Что происходит, это:
- Создать новый
Canine
- Создайте новый
CanineHandler
и добавьте его в мой недавно созданный Canine
- Сохранить
Canine
OnPreInsert
запускается с только что созданными Canine
и мои поля LastUpdated
& LastUpdatedBy
заполняются.
- Я получаю ошибку
not-null property references a null or transient value Model.CanineHandler.LastUpdatedBy
Очевидно, проблема в том, что поля LastUpdated
не заполняются до того, как NHibernate проверит экземпляр CanineHandler
, но я не могу понять, как заставить NHibernate вызывать метод OnPreInsert
для каждого экземпляр, который сохраняется в базе данных.
Одна из опций, с которой я играл, - это добавление другого свойства в мои Auditable
POCO, которые содержат группу Auditable
дочерних объектов, но с технической точки зрения я мог бы сохранить CanineHandler
с измененным Canine
родителем, был изменен, и тогда я буду каскадно вставлять / обновлять в противоположном направлении.
Итак, я нахожусь на правильном пути с каким-то неправильным кодом или я должен подходить к этому по-другому.
Вот соответствующий (усеченный) код:
Интерфейс Auditable
public interface IAuditable
{
DateTime LastUpdated { get; set; }
int? LastUpdatedBy { get; set; }
string AuditTypeName { get; }
Object AuditGetNew(String changes);
}
Обработчик события OnPreInsert (реализует интерфейс IPreInsertEventListener)
public bool OnPreInsert(PreInsertEvent insertEvent)
{
if (insertEvent.Entity is IAuditable)
{
IAuditable auditablePoco = insertEvent.Entity as IAuditable;
auditablePoco.LastUpdated = DateTime.Now;
auditablePoco.LastUpdatedBy = (int?)PersonID;
//Taken directly from Ayende's blog, these calls update the
//event's state to match the entity's state
Set(insertEvent.Persister, insertEvent.State, "LastUpdated", auditablePoco.LastUpdated);
Set(insertEvent.Persister, insertEvent.State, "LastUpdatedBy", auditablePoco.LastUpdatedBy);
}
return false;
}
Собачье картирование
<hibernate-mapping namespace="CanineApp.Model" assembly="CanineApp.Model" xmlns="urn:nhibernate-mapping-2.2">
<class name="Canine" table="Canine">
<id name="CanineID" type="Int32">
<generator class="identity" />
</id>
<property name="Name" type="String" length="64" not-null="true" />
<property name="LastUpdated" type="Date" />
<property name="LastUpdatedBy" type="Int32" />
<set name="CanineHandlers" table="CanineHandler" inverse="true"
order-by="EffectiveDate desc" cascade="save-update"
access="field.camelcase-underscore">
<key column="CanineID" />
<one-to-many class="CanineHandler" />
</set>
</class>
</hibernate-mapping>
CanineHandler mapping
<hibernate-mapping namespace="OPS.CanineApp.Model" assembly="OPS.CanineApp.Model" xmlns="urn:nhibernate-mapping-2.2">
<class name="CanineHandler" table="CanineHandler" schema="dbo">
<id name="CanineHandlerID" type="Int32">
<generator class="identity" />
</id>
<property name="HandlerPersonID" type="Int64" precision="19" not-null="true" />
<property name="EffectiveDate" type="DateTime" precision="16" not-null="true" />
<property name="LastUpdated" type="Date" not-null="true" />
<property name="LastUpdatedBy" type="Int32" not-null="true" />
<many-to-one name="Canine" class="Canine" column="CanineID" not-null="true" access="field.camelcase-underscore" />
</class>
</hibernate-mapping>