IPreUpdateEventListener and dynamic-update="true"
Asked Answered
S

2

11

I have been trying to do a very simple auditing scenario following Ayende's blog which seem to be the resource everyone is refering to when it comes to IPreUpdateEventListener and IPreInsertEventListener.

However no matter how hard I tried, I couldn't get it to work. The event fired correctly, everything looked ok when I stepped through it but no update of my "changedtime" was ever issued to the database.

I spent about a day googling this and finally found the answer here.

It just won't work when you have your entity mapped with dynamic-update="true". And sure enough, that was the case for me. Since it was so hard for me to find this piece of information, is it uncommon to use dynamic-update="true"? We use it on all our entities.

As this is a major bump in the road for us I wanted to ask if there's any way around this at all?

I have been looking at IInterceptor but it's always refered to as outdated, so what's the drawbacks with this? Also I haven't been able to find a really good tutorial on how to archieve the same auditing (with insert/update timestamps) with IInterceptor (I'm fairly new to NHibernate).

Any help would be appreciated!

Savina answered 23/2, 2011 at 6:57 Comment(3)
It is uncommon to use dynamic-update (though it's very helpful to find phantom updates). With dynamic-update, NHibernate has to generate the SQL on each time; without it, it can re-use SQL and substitute values. That's my understanding anyway.Hypertrophy
Interesting, I didn't know that. The general understanding here has been that it must be more optimized to send only what's been changed rather than everything.Savina
@JamieIde: I disagree. I use dynamic-update by default, because it creates cleaner SQL. I don't think the performance impact justifies not using it, but I haven't measured it.Mesenchyme
M
11

I ran into this exact issue. This is how I fixed it:

public class MyFlushEntityEventListener : DefaultFlushEntityEventListener
{
    protected override void DirtyCheck(FlushEntityEvent e)
    {
        base.DirtyCheck(e);
        if (e.DirtyProperties != null &&
            e.DirtyProperties.Any() &&
            //ITrackUpdate is my inteface for audited entities
            e.Entity is ITrackUpdate)
            e.DirtyProperties = e.DirtyProperties
             .Concat(GetAdditionalDirtyProperties(e)).ToArray();
    }

    static IEnumerable<int> GetAdditionalDirtyProperties(FlushEntityEvent @event)
    {
        yield return Array.IndexOf(@event.EntityEntry.Persister.PropertyNames, 
                                   "UpdateTime");
        yield return Array.IndexOf(@event.EntityEntry.Persister.PropertyNames, 
                                   "UpdateUser");
        //You can add any additional properties here.
        //Some of my entities do not track the user, for example.
    }
}

Then, just replace the event listener in the NH config file:

<listener type="flush-entity"
          class="MyFlushEntityEventListener, MyAssembly"/>
Mesenchyme answered 23/2, 2011 at 13:48 Comment(4)
Nice. Any thoughts on how this performs vs. the IPreUpdateListener implementation suggested by Ayende?Hypertrophy
@JamieIde: Actually, I'm using this in addition to the IPreUpdate/InsertEvenListener (separation of concerns: this is just to fix dynamic update). There's no noticeable impact.Mesenchyme
I started getting the NHibernate.HibernateException "Found shared references to a collection:" exception after implementing this. The root cause was calling AppendListeners instead of SetListeners, so the default flush entity listener was not being replaced. Hope this helps someone, I'll never get the last hour back.Hypertrophy
This has worked well for me. One point to make in 2021 is that NHibernate now has Async methods. So if you want to implement this in NHibernate v5 you need to overrride the DirtyCheckAsync method as well as the DirtyCheck method.Plump
U
1

I had the same issue, however I found I could work around it by using OnFlushDirty.

You can find my solution here.

Umbilicus answered 9/9, 2011 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.