Checking If An Entity Is Dirty

1,341 views
Skip to first unread message

Travis

unread,
Jun 14, 2008, 7:04:13 AM6/14/08
to nhusers
I would like to determine if an entity is considered dirty by
NHibernate to update my interface.

For example, if a user changes a field and databinding occurs, I would
like to indicate in the interface (i.e., show a "*" in the form title)
that the user needs to save the record. I'd also want to ask the user
if they would like to save the record if they close the form, etc.

Is there some way I can externally ask an entity if it has changed? Or
do I have to track this manually by setting some dirty flag in each
property? If there is some way to ask NHibernate if an entity is
dirty, is there a way to make it also determine if any of its one-to-
many mapped objects are dirty as well?

Thanks!
Travis

James Kovacs

unread,
Jun 14, 2008, 12:32:58 PM6/14/08
to nhu...@googlegroups.com
You can easily determine if any entities in a session are dirty by checking ISession.IsDirty(). This will tell you whether any writes will be made to the database when the session is flushed. This will allow you to indicate whether a save is necessary for the form. I don't know of an easy way to determine whether an individual entity is dirty without requiring digging through NHibernate's internals.

James
--
James Kovacs, B.Sc., M.Sc., MCSD, MCT
Microsoft MVP - C# Architecture
http://www.jameskovacs.com
jko...@post.harvard.edu
403-397-3177 (mobile)

Ayende Rahien

unread,
Jun 14, 2008, 1:08:50 PM6/14/08
to nhu...@googlegroups.com
Here is a good way to handle that:

www.ayende.com/Blog/archive/2007/04/17/Advance-Extending-NHibernate-Proxies.aspx

James Kovacs

unread,
Jun 14, 2008, 1:28:27 PM6/14/08
to nhu...@googlegroups.com
That is a nice way to implement INotifyPropertyChanged. The only issue is that it only tells you if the property has changed, not if the entity is dirty. For example, assuming your implementation:

IFoo foo = session.Get<Foo>(42);  // value of foo.Bar is 1
foo.Bar = 10;
foo.Bar = 1;

You will get 2 property changed notifications, as expected. Is the entity dirty? No, because you changed the value back to the original value.

If this scenario is important to your business users (i.e. you need to know whether a save is really required versus potentially required), then you could write a different interceptor that would create a dirty-checking proxy that compared current entity state to what is in NHibernate's level 1 cache.


James
--
James Kovacs, B.Sc., M.Sc., MCSD, MCT
Microsoft MVP - C# Architecture
http://www.jameskovacs.com
jko...@post.harvard.edu
403-397-3177 (mobile)

Travis

unread,
Jun 14, 2008, 4:21:19 PM6/14/08
to nhusers
Cool stuff. I think it's good enough to reflect a dirty state if any
property as been modified (even if it has been changed back to its
original value).

I'm still a little unclear. Once this has been done, I'm thinking I
should register an event handler on the object and then, if called,
assume the record is dirty, correct?

I really wish there would be a way to call an object at any level of a
system with a call to something like object.IsDirty(). Perhaps I could
use this same method to add my own custom interface that allows me to
call a IsDirty() type of function on an object.

I'm very new to NHibernate, so I'm not sure how to get to the current
and previous state values (I'm doing my own custom dirty checking via
an interceptor, so something along those lines would be great).
Something like:

interface IDirty
{
Dirty
{
get;
}
}

MyObject : IDirty
{
Dirty
{
get
{
// Compare currentstate values against previousstate values. Not
sure how
// I can access these from within an object. Can this only be
done externally
// via proxies?
}
}
}

Here's another real newbie question that I'm unclear about. Assume
I've mapped two objects, A and B. A has a one-to-many relationship
mapped to A and B has a many-to-one relationship mapped to A.
Currently, I have to explicitly set the owner of B in order for it to
save correctly when adding it to A's collection. I'm wondering if this
relationship can be fixed up automatically.

For example:

A currentInstance = GetCurrentA();

B createdObject = new B();
createdObject.Owner = currentInstance; // This line is necessary with
my current mapping.

IList<B> tempList = currentInstance.OwnedBs;
tempList.Add( createdObject ); // Can NHibernate infer the Owner from
adding it to this list?

Session.Save( currentInstance ); // Works great. But would like the
extra fixup removed.

Thanks again in advance!

Travis

On Jun 14, 10:28 am, "James Kovacs" <jkov...@post.harvard.edu> wrote:
> That is a nice way to implement INotifyPropertyChanged. The only issue is
> that it only tells you if the property has changed, not if the entity is
> dirty. For example, assuming your implementation:
>
> IFoo foo = session.Get<Foo>(42);  // value of foo.Bar is 1
> foo.Bar = 10;
> foo.Bar = 1;
>
> You will get 2 property changed notifications, as expected. Is the entity
> dirty? No, because you changed the value back to the original value.
>
> If this scenario is important to your business users (i.e. you need to know
> whether a save is really required versus potentially required), then you
> could write a different interceptor that would create a dirty-checking proxy
> that compared current entity state to what is in NHibernate's level 1 cache.
>
> James
> --
> James Kovacs, B.Sc., M.Sc., MCSD, MCT
> Microsoft MVP - C# Architecturehttp://www.jameskovacs.com
> jkov...@post.harvard.edu
> 403-397-3177 (mobile)
>
> On Sat, Jun 14, 2008 at 11:08 AM, Ayende Rahien <aye...@ayende.com> wrote:
> > Here is a good way to handle that:
>
> > www.*ayende*
> > .com/Blog/archive/2007/04/17/Advance-Extending-NHibernate-Proxies.aspx

Maximilian Raditya

unread,
Jun 16, 2008, 2:33:35 AM6/16/08
to nhu...@googlegroups.com
On Sat, Jun 14, 2008 at 6:04 PM, Travis <Travis...@gmail.com> wrote:
Is there some way I can externally ask an entity if it has changed? Or
do I have to track this manually by setting some dirty flag in each
property? If there is some way to ask NHibernate if an entity is
dirty, is there a way to make it also determine if any of its one-to-
many mapped objects are dirty as well?
 
Hi,
 
What I'm currently is by creating a PropertyState for each property of an object. That way, I maintain a set of PropertyStates of every object implementing specific dirty-aware interface, let say IDirtyEntity with IDirtyEntity.IsDirty.
 
A PropertyState basically contains Name, Value (original value), and IsDirty properties. A set of PropertyStates of an object are initiliazed in either the following ways:
  • create a new entity => I create new entity through an EntityFactory so that I can manually attach the initial PropertyStates. The property Name (of the PropertyState) is obtained from ISessionFactory.GetClassMetadata()
  • load an existing entity => I use NHibernate.IInterceptor.OnLoad() to manually attach them.
Just like a basic implementation of INotifyPropertyChanged, I monitor the IDirtyEntity.IsDirty of an object by putting an extra method, let say IDirtyEntity.UpdatePropertyStateDirtyStatus(), in every property's set implementation which tracking the the changes by comparing their current values to their initial values initialized before. It basically sets PropertyState.IDirty to true/false based on the changes. IDirtyEntity.IsDirty get implementation is actually only an enumeration of every PropertyState.IsDirty.
 
You probably see that there are pros and cons with this approach. One of the pros itself is the ease of use of IDirtyEntity.IsDirty. And one of the cons itself is the extra memory of the PropertyStates set of each object.
 
I think I'd like to come with concept first here. Though, I have implemented it by myself and, so far, it really works like a charm :). I'd like to hear about what you all think about this.
 
HTH.
 
 
 
 



--
Regards,

Maximilian Haru Raditya

Travis

unread,
Jun 17, 2008, 12:14:38 AM6/17/08
to nhusers
I've actually implemented something like this that utilized by own
custom ORM layer. Using much the same method as you have, I would
always capture the original DB values and compare against those in
calls to IsDirty.

While I'm not too concerned with the extra memory required to capture
these fields, it does seem like it might be redundant. As part of an
earlier test, I created an interceptor that implements the FindDirty
method. This passes in the current state and previous state of the
object. As a result, I'm assuming that NHibernate keeps the original
state somewhere. I just don't know how or if there's a way to ask
NHibernate what the original state of an entity is.

I'll do some tracing through the source and see where the state is
stored and see if there's something obvious. If all else fails, I'll
resort to doing something exactly like you specified.

Thanks!
Travis

On Jun 15, 11:33 pm, "Maximilian Raditya" <m4h...@gmail.com> wrote:
> On Sat, Jun 14, 2008 at 6:04 PM, Travis <TravisLPru...@gmail.com> wrote:
> > Is there some way I can externally ask an entity if it has changed? Or
> > do I have to track this manually by setting some dirty flag in each
> > property? If there is some way to ask NHibernate if an entity is
> > dirty, is there a way to make it also determine if any of its one-to-
> > many mapped objects are dirty as well?
>
> Hi,
>
> What I'm currently is by creating a PropertyState for each property of an
> object. That way, I maintain a set of PropertyStates of every object
> implementing specific dirty-aware interface, let say IDirtyEntity with
> IDirtyEntity.IsDirty.
>
> A PropertyState basically contains Name, Value (original value), and IsDirty
> properties. A set of PropertyStates of an object are initiliazed in either
> the following ways:
>
>    - create a new entity => I create new entity through an EntityFactory so
>    that I can manually attach the initial PropertyStates. The property Name (of
>    the PropertyState) is obtained from ISessionFactory.GetClassMetadata()
>    - load an existing entity => I use NHibernate.IInterceptor.OnLoad() to
Reply all
Reply to author
Forward
0 new messages