4

I am looking for help on an issue with NHibernate which has been bugging me for a while now. Long story short:

I’m looking for a way to, in the first level cache, “reset” a property on an entity each time I do an update or an insert.

What I want to achieve is that the property in question will always be considered to be dirty by NHibernate when using dynamic update or insert.

The backstory for this is that I know that, if the transaction was successful, the column that I want to “reset” will be set to Null in the database by a trigger. On the flip side, the first level cache does not know this, and thus NHibernate will think that the property was not updated when I set it to the same value as I did on the previous update/insert. The catch is that my trigger is dependent on this value being set. The resulting mess is that if I want to use dynamic update or insert I’m only able to update/insert an entity once without “refreshing” it afterwards (which I really don’t want to do).

Tips or help would be much appreciated, because I’ve really hit a wall here

Community
  • 1
  • 1
dnyvik
  • 43
  • 4
  • 2
    See also http://stackoverflow.com/questions/5087888/ipreupdateeventlistener-and-dynamic-update-true – Jamie Ide Nov 19 '12 at 17:27
  • I tried this approach too, but with no luck. For some reason adding the property index to the `e.DirtyProperties`-collection didn’t work as expected. This could have something to do with my code, but I haven’t managed to find the root cause yet. The funny thing was that if I skipped the call to `base.DirtyCheck(e)` and just added the dirty properties manually, it all worked perfectly. Color me confused.. – dnyvik Nov 20 '12 at 16:33

1 Answers1

3

NHibernate provides many places for extension. Among them is the Session IInterceptor. There is documentation with many details:

http://nhibernate.info/doc/nh/en/index.html#objectstate-interceptors

In this case, we can create our custom one, which will be observing our entity (for example Client) and a property which must be updated every time (for example Code). So our implementation could look like this:

public class MyInterceptor : EmptyInterceptor
{
    public override int[] FindDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
    {
        var result = new List<int>();

        // we do not care about other entities here
        if(!(entity is Client))
        {
            return null; 
        }

        var length = propertyNames.Length;

        // iterate all properties
        for(var i = 0; i < length; i++)
        {
            var areEqual = currentState[i].Equals(previousState[i]);
            var isResettingProperty = propertyNames[i] == "Code";

            if (!areEqual || isResettingProperty)
            {
                result.Add(i); // the index of "Code" property will be added always
            }
        }

        return result.ToArray();
    }
}

NOTE: This is just an example! Apply your own logic for checking the dirty properties.

And we have to wrap Session this way:

var interceptor = new MyInterceptor()
_configuration.SetInterceptor(interceptor);

And this is it. While Client is marked as dynamic-update, the property Code will always be set as dirty

<class name="Client" dynamic-update="true" ...
Owen Pauling
  • 11,349
  • 20
  • 53
  • 64
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • 1
    I implemented similar functionality by extending DefaultFlushEventListener. I don't recall if I tried an IInterceptor solution. – Jamie Ide Nov 19 '12 at 17:29
  • 1
    Indeed, `EventListeners` are another useful extension point. Flexibility of NHibernate is really exceptional! – Radim Köhler Nov 19 '12 at 18:07
  • Thank you ever so much @Radim, it worked like a charm! The only thing you have to be mindful of with this solution is that you can’t do “.Equals” straight up if you have nullable properties. You have to do something like: `areEqual = currentState[i] == null ? previousState[i] == null : currentState[i].Equals(previousState[i]);` Next I’m going to try the approach which @Jamie linked to. Once again, thank you guys for the response! – dnyvik Nov 20 '12 at 11:39
  • Briliant! You are right;) thanks for correction *(it was just a quick example)* – Radim Köhler Nov 20 '12 at 11:53