Logo

NHibernate

The object-relational mapper for .NET

How to

This page is converted from the old nhforge.org Wiki. First published by: Scott Findlater on 04-26-2011, Last revision by: David Gardiner on 07-06-2012

Changing Values in NHibernate Events

Abstract

Audit trails using NHibernate's event model often use the OnPreInsert and OnPreUpdate event listeners to change/ modify the state of the entity.  Although It may work in some cases and is sometimes referred to as a solution, it should be noted the OnPreInsert and OnPreUpdate events are not intended to be used to change the values of the entity and instead they should be used to check values and for that reason they return "veto"

There is also a bug report about that.

Why OnPreUpdate Triggers May Fail

There is a reason for that. NHibernate does the following:

  • When flushing, the FlushEntityEventListener is called for each entity in the session.
  • It determines the dirty properties.
  • Based on the dirty properties, it determines if the entity needs updating
  • It calls PreUpdate triggers
  • Based on the dirty properties it updates the corresponding tables.

The last point is the critical one. If your PreUpdate trigger changes properties that are not found dirty before and which are not in the same table as the dirty properties, it won't be updated in the database. The same happens when dynamic-update is used.

Example:

<class name="Foo">
  <id ...>
  <property name="Name"/>
  <join table="AnotherTable>
    <key name="id"/>
    <property name="Value"/>
  </join>
</class>

Now, when you change Name in the session (it gets dirty) and Value in the trigger, the table AnotherTable won't be updated.

How To Implement It The Correct Way

The correct way is to use the FlushEntityEventListener to implement property changes.The important difference is that you can provide a list of changed properties in this event listener, and therefore tell NHibernate what to store.

There is an example by Buthrakaur.

History

There is a solution to reproduce the code by Scott Findlater here. The tests folder in each project highlights successes/ issues (by failing tests) in each project.

Project - 01OnPreEvents. This project shows a typical, working, approach to audit trails using the OnPreInsert and OnPreUpdate to modify the entity state. An Entity base class which is inherited by a Category class.  The base Entity provides identification and common auditing properties.

Project - 02OnPreEventsFailing. This project shows how an inheritance entity hierarchy using the OnPreInsert and OnPreUpdate events to modify entity state fail to persist the changed entity state.  Here, for whatever design decision, the Entity base class which originally provide identification and common auditing has been split into Entity and AuditableEntity.  Now the Category class inherits from AuditableEntity.

Project - 03Save.  Uses the same entity inheritance hierarchy from the 02OnPreEventsFailing project to demonstrate a working version using the Save/ Insert events.

References to the problem

Audit trails using NHibernate's event model are well documented;

There is also this NHibernate bug report - changes made in IPreInsert/UpdateEventListener are not persisted into DB on inherited classes with the resolution of "not an issue" because "pre-insert and pre-update listeners are not intended to be used to change the values of the entity. Instead they should be used to check-values (for that reason they return "veto")." (see in context)

This contradiction of intended usage regarding the changing of entity state was further discussed on this NHUser Group thread, with the same answer.

There are some discussions and blog posts related to this problem:

© NHibernate Community 2024