Search code examples
c#nhibernatenhibernate-mappingnhibernate-caches

How to make NHibernate considered property to always be dirty when using dynamic update or insert?


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


Solution

  • 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" ...