IPreInsertEventListener not firing for child objects
I've wired up IPreInsertEventListener
and IPreUpdateEventListener
to manage audit logging in my app, which is using NHibernate as the ORM layer.
The problem I'm having is that the OnPreInsert()
event is fired when the object is persisted, but is not fired for each inserted child object as well.
In my case, I have a Canine
, which has a collection of CanineHandlers
. What's happening is:
- Create a new
Canine
- Create a new
CanineHandler
and add it to my newly createdCanine
- Save the
Canine
OnPreInsert
is fired with the newly createdCanine
and myLastUpdated
&LastUpdatedBy
fields are populated.- I get an error
not-null property references a null or transient value Model.CanineHandler.LastUpdatedBy
Obviously the problem is that the LastUpdated
fields aren't being filled in before NHibernate validates the CanineHandler
instance, but what I can't figure out is how to get NHibernate to call the OnPreInsert
method for each instance that's being persisted to the database.
One option that I've toyed with is adding another property to my Auditable
POCOs that contains a group of Auditable
child objects, but technically speaking I could save a CanineHandler
with a modified Canine
parent that has been changed, and then I'd be cascading the insert/update in the opposite direction.
So, am I on the right track with some incorrect code somewhere, or should I be approaching this a different way.
Here's the relevant (truncated) code:
Auditable interface
public interface IAuditable
{
DateTime LastUpdated { get; set; }
int? LastUpdatedBy { get; set; }
string AuditTypeName { get; }
Object AuditGetNew(String changes);
}
OnPreInsert event handler (implements IPreInsertEventListener interface)
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;
}
Canine mapping
<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>
Not sure this will work, but I noticed that your Canine mappings for LastUpdatedBy and LastUpdated are not the same as for CanineHandler. Perhaps try removing the not-null="true" from CanineHandler mappings.
I have done auditing for nHibernate in the same fashion and it worked fine, although I used the older intercepting interface, namely IInterceptor. Also I did not have not-null="true" in my mappings.
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" />
<property name="LastUpdatedBy" type="Int32" />
<many-to-one name="Canine" class="Canine" column="CanineID" not-null="true" access="field.camelcase-underscore" />
</class>
</hibernate-mapping>
精彩评论