apologies in advance for the long post. I'm hoping somebody can help.
I've been asked to work on upgrading NHibernate (from 2.1.2.4000 to 3.3.1.4000) and Fluent NHibernate (from 1.1.0.685 to 1.3.0.0) in an ASP.NET app. I'm a newbie to NHibernate but have spent several weeks working on this so I've got some understanding.
For instance, I know the newer version of NHibernate has a built-in proxy generator, so I've deleted any references to the old NHibnernate.ByteCode.Castle.dll which was the proxy generator we were using previously, got rid of the reference to that file from the nhibernate.config, and the intention is to use the built-in proxy.
I've been successfully working my way through a variety of issues but have run into one I'm stuck on, and nothing I've found on the web really seems to exactly correspond to it. This is surprising because I would have thought anybody who did such an upgrade would have this issue, but maybe it's just the way we're coding our Entity Base class.
There are two Visual Studio solutions, one of which is a "Framework" solution, which contains the EntityBase class, and the other which is the main application solution, which uses the DLLs from the Framework solution.
In the EntityBase class there are two methods that return the "real" object instead of the proxy, very similar to the "As" method described here: http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded-inheritance.html
As I understand it, these methods are essentially returning the "real" (domain) object which is wrapped by the NHibernate proxy object. In my case the methods in question are called "CastTo" and "AsOfType". It is the type constraints on these methods that seem to be causing my issue.
Here is the pertinent code from my EntityBase class:
/// <summary>
/// Represents the base class for a domain entity.
/// </summary>
/// <typeparam name="TIdentity">The type of the identity.</typeparam>
/// <remarks>
/// This class implements all the interfaces that all domain entities must have.
/// </remarks>
[Serializable]
public abstract class EntityBase<TIdentity> : IAuditable, IEntity<TIdentity>, IPersistent
{
/// <summary>
/// Casts to.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T CastTo<T>() where T : EntityBase<TIdentity>
{
return (T)this;
}
/// <summary>
/// Casts this entity to the type passed in.
/// This is required when trying to cast from a proxy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T AsOfType<T>() where T : EntityBase<TIdentity>
{
return this as T;
}
So, when I run the unit tests in the main application solution, I'm getting errors like this:
Creating a proxy instance failed ---> System.TypeLoadException: Method 'AsOfType' on type 'LocationProxy' from assembly 'LocationProxyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' tried to implicitly override a method with weaker type parameter constraints.
So it seems the Reflection.Emit code that's creating the NHibernate proxy is complaining about the type constraints on the AsOfType and CastTo methods.
So I thought I'd relax those constraints a bit, and instead of using a generic in the type constraint, I'd try just using "Entity" as a constraint (a class derived from EntityBase). So I tried the following (and yes, I know the two methods are essentially doing the same thing, but I'm just trying to preserve the interface of the EntityBase class to avoid breaking all the calls to those methods):
[Serializable]
public abstract class EntityBase<TIdentity> : IAuditable, IEntity<TIdentity>, IPersistent
{
/// <summary>
/// Casts to.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T CastTo<T>() where T : Entity
{
if (!typeof(T).IsAssignableFrom(GetType()))
{
throw new InvalidCastException();
}
return this as T;
}
/// <summary>
/// Casts this entity to the type passed in.
/// This is required when trying to cast from a proxy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T AsOfType<T>() where T : Entity
{
return this as T;
}
So now the test fails in a different way. I've got a unit test that looks like this:
/// <summary>
/// Tests the type of the get real.
/// </summary>
[TestMethod, TestCategory("Integration")]
public void TestEntityProxyCastToMethod()
{
using (var unitOfWork = UnitOfWork.Begin(Database.MainSolution.Name()))
{
var person = unitOfWork.GetProxy<Person>(new Guid("E196BC94-DFBA-4D6C-B504-03E00F5CA914"));
Assert.IsTrue(person.IsOfType<Employee>());
var employee = person.AsOfType<Employee>();
Assert.IsNotNull(employee);
}
}
And the exception thrown now when running the unit test is:
Test method MainSolution.Data.Tests.EntityProxyTests.TestEntityProxyCastTo threw exception: System.InvalidOperationException: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
So this still appears to be some Reflection.Emit error, which is happening when NHibernate tries to generate the proxy. It seems clear that the way NHibernate is now generating its proxy is incompatible with the way we coded our EntityBase class. It used to work fine, when we were using the NHibernate.ByteCode.Castle.dll to generate the proxy, but it's obviously not happy with the type constraints on these methods.
Now, I've seen posts like this ( Nhibernate: Get real entity class instead of proxied class ) which suggest that we should just "Unproxy" the class to get the underlying "real" object. But that would mean changing the signature of these methods (perhaps), breaking all the calls to the "CastTo" and "AsOfType" methods etc. As I understand it, I'd have to get a UnitOfWork, get the current session from that, then do the "Unproxy" thing to get the underlying object, whereas with the current code, all we had to do was essentially return "this as T" and it would work. I thought maybe I could get the current session in the calling code, then pass it into some method on the EntityBase, but that seems ugly and a lot of overhead for doing something that seems like it ought to be simpler.
So the questions are: a) what am I doing wrong, b) how can I do it correctly given the 3.3. version of NHibernate, and c) is there a way to preserve the existing signature of the "CastTo" and "AsOfType" methods on my EntityBase class, while still getting NHibernate to generate its proxies correctly without complaining about the type constraints on those methods?
Any help appreciated, many thanks.