Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement ChangeTime and ChangeUser columns using NHibernate?

Tags:

nhibernate

I'm trying to use NHibernate with an existing database. In the data-model there is columns in each table that contains the time and username of the last update made to a row. How do I do this using NHibernate?

I tried to implement a interceptor that sets ChangeTime and ChangeUser in the entities before it gets saved using the IInterceptor.OnSave method. This didn't work because setting these properties triggers an update to the row even if no other properties has been modified.

It could have worked if there was any way to tell NHibernate to exclude the ChangeTime and ChangeUser properties then it does it's dirty-checking. But i haven't found any way to accomplish this.

Thanks for any help.

like image 548
Mikael Sundberg Avatar asked Feb 15 '09 22:02

Mikael Sundberg


3 Answers

You should register a listener to the pre insert and pre update events. You can do it through your configuration like so:

<hibernate-configuration>
    ...
    <event type="pre-update">
        <listener class="MyListener, MyAssembly"/>
    </event>
    <event type="pre-insert">
        <listener class="MyListener, MyAssembly"/>
    </event>
</hibernate-configuration>

and then implement a listener - something like this (might not be entirely accurate - written off my memory):

public class MyListener : IPreUpdateEventListener, IPreInsertEventListener
{
    public bool OnPreUpdate(PreUpdateEvent evt)
    {
        if (evt.Entity is IHasLastModified)
            UpdateLastModified(evt.State, evt.Persister.PropertyNames);

        return false;
    }

    public bool OnPreInsert(PreInsertEvent evt)
    {
        if (evt.Entity is IHasLastModified)
            UpdateLastModified(evt.State, evt.Persister.PropertyNames);

        return false;
    }

    void UpdateLastModified(object[] state, string[] names)
    {
        var index = Array.FindIndex(names, n => n == "LastModified");

        state[index] = DateTime.Now;
    }
}

and do the same thing with the pre update event.

EDIT: This one takes care of insert as well as update and it seems to work.

like image 164
mookid8000 Avatar answered Nov 12 '22 05:11

mookid8000


Hey I just had to solve this on a project I am working on, here is my answer

public interface IDateModified
{
    DateTime Created { get; set; }
    DateTime Modified { get; set; }
}

public class CustomDefaultSaveOrUpdateEventListener 
    : DefaultSaveOrUpdateEventListener
{
    protected override object EntityIsPersistent(SaveOrUpdateEvent evt)
    {
        var entity = evt.Entity as IDateModified;
        if (entity != null)
        {
                entity.Modified = DateTime.Now;
        }

        return base.EntityIsPersistent(evt);
    }

    protected override object EntityIsTransient(SaveOrUpdateEvent evt)
    {
        var entity = evt.Entity as IDateModified;
        if (entity != null)
        {
            entity.Created = entity.Modified = DateTime.Now;
        }

        return base.EntityIsTransient(evt);
    }
}

Then in my configuration (I am using Fluent NHibernate to configure my unit tests in code)

configuration.EventListeners.SaveOrUpdateEventListeners 
= new ISaveOrUpdateEventListener[]
{
    new CustomDefaultSaveOrUpdateEventListener() 
};

AWESOMENESSSSSSSS!

like image 37
superlogical Avatar answered Nov 12 '22 04:11

superlogical


Mookid's answer is correct although I would like to point out that if one is using S#arp Architecture, the NHib configuration should be set up as follows:

<hibernate-configuration>
    <session-factory>
    ...
        <event type="pre-update">
            <listener class="MyListener, MyAssembly"/>
        </event>
        <event type="pre-insert">
            <listener class="MyListener, MyAssembly"/>
        </event>
    </session-factory>
</hibernate-configuration>

The event elements go into the session-factory element.

like image 1
LordHits Avatar answered Nov 12 '22 06:11

LordHits