Reputation: 1421
I'm sure I've seen this discussed, but I must not be using the right keywords because I can't find anything on it now:
I have a desktop application using NHibernate persistence. I'd like to use the session's isDirty property to notify the user whether any persistent data has been changed, showing him either a Close button or an Apply and a Cancel button, depending.
The problem is that calling isDirty causes at least certain data to get flushed to the database, despite the fact that my FlushMode is Never. I'm interested in knowing why isDirty feels it has to flush those changes, but more importantly I want to know how I can get this information without flushing.
As an aside: Since I don't have a transaction wrapping the whole time the user edits the information on the form, I assume that any changes flushed are there to stay, whether I end up committing everything else or not.
So, can someone tell me how to get the functionality I want without the functionality I don't?
Upvotes: 1
Views: 1448
Reputation: 1421
I was finally able to turn my attention back to this and work out a solution, as follows:
/// <summary>
/// Check whether a session is "dirty" without triggering a flush
/// </summary>
/// <param name="session"></param>
/// <returns>true if the session is "dirty", meaning that it will update the database when flushed</returns>
/// <remarks>
/// The rationale behind this is the need to determine if there's anything to flush to the database without actually
/// running through the Flush process. The problem with a premature Flush is that one may want to collect changes
/// to persistent objects and only start a transaction later on to flush them. I have this in a Winforms application
/// and this method allows me to notify the user whether he has made changes that need saving while not leaving a
/// transaction open while he works, which can cause locking issues.
/// <para>
/// Note that the check for dirty collections may give false positives, which is good enough for my purposes but
/// coule be improved upon using calls to GetOrphans and other persistent-collection methods.</para>
/// </remarks>
public static bool IsDirtyNoFlush(this ISession session)
{
var pc = session.GetSessionImplementation().PersistenceContext;
if (pc.EntitiesByKey.Values.Any(o => IsDirtyEntity(session, o)))
return true;
return pc.CollectionEntries.Keys.Cast<IPersistentCollection>()
.Any(coll => coll.WasInitialized && coll.IsDirty);
}
The first part is basically as Shahin recommended above. The last line is what I had been missing. As I mention in the comment, it's still not as good as the built-in IsDirty(), because that one will recognize, for instance, that removing an item from a persistent collection and then putting it back in makes it not dirty anymore, whereas my method will give a false-positive. Still, it's completely adequate for my purposes (and most others, I would imagine) and avoids flushing.
Upvotes: 1
Reputation: 6922
I haven't done this myself, but after some googling, it seems possible. How about just looping over all the loaded entities in the session and checking if they're dirty? You can even manually compare the loaded entity vs. the current entity states.
Try this code that I found here on StackOverflow: (Source)
var dirtyObjects = new List<object>();
var sessionImpl = session.GetSessionImplementation();
foreach (NHibernate.Engine.EntityEntry entityEntry in sessionImpl.PersistenceContext.EntityEntries.Values)
{
var loadedState = entityEntry.LoadedState;
var o = sessionImpl.PersistenceContext.GetEntity(entityEntry.EntityKey);
var currentState = entityEntry.Persister.GetPropertyValues(o, sessionImpl.EntityMode);
if (entityEntry.Persister.FindDirty(currentState, loadedState, o, sessionImpl) != null)
{
dirtyObjects.Add(entityEntry);
}
}
If you're worried about performance you could check the type of the entity before comparing the properties:
Type t = Type.GetType(entityEntry.EntityName);
if (t == typeof(Employee))
{
//do something
}
You can also try looking into making your own dirty-check interceptor.
Upvotes: 1
Reputation: 151
I did dirty state tracking within my viewmodel properties. On each PropertyChanged
i call a MarkAsDirty()
of my ViewModelBase class. This reflects a property which is then bound onto my Save Button Command.
But if you want to use the NH Tracking, this SessionExtension could fit your requirements.
Upvotes: 0