After looking over the of the NHibernate.Envers code I realized I was implementing the wrong interface. Now that I know what interface(s) to use things are working a little better.
My current implementation looks like this:
public class PreCollectionUpdate : IPreCollectionUpdateEventListener
{
public void OnPreUpdateCollection(PreCollectionUpdateEvent @event)
{
var collectionEntry = @event.Session.PersistenceContext.GetCollectionEntry(@event.Collection);
if(!collectionEntry.LoadedPersister.IsInverse)
return;
var collection = @event.Collection;
var collectionEntries = collection.Entries(collectionEntry.LoadedPersister);
foreach(var entry in collectionEntries)
{
if(!(entry is TrackableEntity))
return;
var trackableEntity = entry as TrackableEntity;
trackableEntity.AddedAt = Time.Now;
trackableEntity.AddedBy = User.Current;
}
}
}
Through debugging I can see it is being called and properly modifying my collection. For some reason however, it first inserts the items in my collection with default values for AddedAt and AddedBy then later performs an update to fill said values.
Here is my test code:
using (var transaction = Session.BeginTransaction())
{
var locate = new Locate
{
TicketNumber = 123456789,
Status = Status.InProgress
};
Session.Save(locate);
transaction.Commit();
}
using (var transaction = Session.BeginTransaction())
{
var locate1 = Session.Get<Locate>(1);
locate1.AddReview(new AllClear());
Session.Save(locate1);
transaction.Commit();
}
using (var transaction = Session.BeginTransaction())
{
var locate1 = Session.Load<Locate>(1);
transaction.Commit();
}
Why is that?
Debugging my test I can see that after I commit my transaction that both AddedBy and AddedWhen properties are properly filled. Simply not sure why it isn’t committing the modified collection.
Adding a bunch of Console.Write statements all over my code I can see that my event listener is being called after I commit my session.
NHibernate: INSERT INTO Locates (AddedAt, AddedBy, TicketNumber, Status, SomeOtherField) VALUES (@p0, @p1, @p2, @p3, @p4); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = 123456789 [Type: Int64 (0)], @p3 = 'InProgress' [Type: String (0)], @p4 = NULL [Type: String (0)]
Saving review...
Commiting...
NHibernate: INSERT INTO "Review" (AddedAt, AddedBy, Locate_id) VALUES (@p0, @p1, @p2); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = NULL [Type: Int32 (0)]
In event listener...
Completed filling auditable properties.
I’ve also tried inheritign from IFlushEntityEventListener but the same issue happens. I commit without the AddedAt and AddedBy properties being persisted to the database. My object will however change so next time I work with said object NHibernate will see that it is dirty and issue an update command to the database. What I want is for the AddedAt and AddedBy properties to be committed on the initial commit.
Please let me know if I’m not clear.
I don’t belive you can use the PreUpdateCollectionEventListener for what you want to do. PreUpdateEventListeners all run very late in the nhibernate pipline Thus at the point that the event has been thrown NHibernate has already seralized the state of the entity to memory. You need to change both the state and the entity itself however in PreUpdateCollectionEvent you do not have access to the state as far as I can tell I have always done what you are trying to do with just the regular PreUpdateEventListener for example something like this should work:
PS. I know I once read a really good post by Ayende about this but I can’t find it now.