Posted by: Dennis | August 15, 2010

Avoid initialization of proxy object during compare

Lets assume you have defined an equals operator on your entity class, e.g. something like resharpers default implementation:

public override bool Equals(object obj)
{
 if (ReferenceEquals(null, obj)) return false;
 if (ReferenceEquals(this, obj)) return true;
 if (obj.GetType() != typeof (Category)) return false;
 return ((Category)obj).Id == Id;
}

Now, the problem occurs when you want to use this for an NHibernate proxy object. First of all the GetType will fail, because it is a sub class, not the actual class.

Secondly, if one of the two objects is indeed a proxy object we may never have loaded it from the database. And if we never loaded it from the database, then the call to .Id will cause NHibernate to load the object. And that was NOT what we wanted.

The solution is to make a very complicated equals method that take the proxies into account:

public override bool Equals(object obj)
{
 if (ReferenceEquals(null, obj)) return false;
 if (ReferenceEquals(this, obj)) return true;

Stays the same

 INHibernateProxy thisproxy = this as INHibernateProxy;
 int thisId;
 Type thisType = GetType();
 if (ReferenceEquals(null, thisproxy))
 {
   thisId = Id;
 }
 else
 {
   thisType = thisType.BaseType;
   if (thisproxy.HibernateLazyInitializer.IsUninitialized)
     thisId = (int) thisproxy.HibernateLazyInitializer.Identifier;
   else
     thisId = Id;
 }

This is where it becomes complicated. We cast this to INHibernateProxy, and if we find that it is a proxy object that is not initialized, then we use the Lazy loaders notion of an Id to compare to.

So the full Equals method ends up as:

public override bool Equals(object obj)
{
 if (ReferenceEquals(null, obj)) return false;
 if (ReferenceEquals(this, obj)) return true;
// ReSharper disable SuspiciousTypeConversion.Global
 INHibernateProxy thisproxy = this as INHibernateProxy;
 int thisId;
 Type thisType = GetType();
 if (ReferenceEquals(null, thisproxy))
 {
   thisId = Id;
 }
 else
 {
   thisType = thisType.BaseType;
   if (thisproxy.HibernateLazyInitializer.IsUninitialized)
     thisId = (int) thisproxy.HibernateLazyInitializer.Identifier;
   else
     thisId = Id;
 }
 INHibernateProxy otherproxy = obj as INHibernateProxy;
 int otherId;
 Type otherType = obj.GetType();
 if (ReferenceEquals(null, otherproxy))
 {
   otherId = ((EntityBase) obj).Id;
 }
 else
 {
   otherType = otherType.BaseType;
   if (otherproxy.HibernateLazyInitializer.IsUninitialized)
     otherId = (int)otherproxy.HibernateLazyInitializer.Identifier;
   else
     otherId = ((EntityBase)obj).Id;
 }
 if (otherType != thisType) return false;
// ReSharper restore SuspiciousTypeConversion.Global
 return otherId == thisId;
}

It would have been even better if the proxy was slightly more intelligent and didn’t init itself when fetching the value that is the key.

Advertisement

Categories

Follow

Get every new post delivered to your Inbox.