提前为长篇道歉。我希望有人能帮忙。
我被要求在 ASP.NET 应用程序中升级 NHibernate(从 2.1.2.4000 到 3.3.1.4000)和 Fluent NHibernate(从 1.1.0.685 到 1.3.0.0)。我是 NHibernate 的新手,但已经花了几个星期的时间来解决这个问题,所以我对此有所了解。
例如,我知道新版本的 NHibernate 有一个内置的代理生成器,所以我删除了对旧的 NHibernate.ByteCode.Castle.dll 的任何引用,这是我们之前使用的代理生成器,摆脱了引用从 nhibernate.config 到该文件,目的是使用内置代理。
我已经成功地解决了各种问题,但遇到了一个我被困住的问题,而且我在网上找到的任何东西似乎都与它完全对应。这令人惊讶,因为我原以为进行此类升级的任何人都会遇到此问题,但也许这只是我们编写 Entity Base 类的方式。
有两种 Visual Studio 解决方案,一种是“框架”解决方案,其中包含 EntityBase 类,另一种是主应用程序解决方案,它使用来自框架解决方案的 DLL。
在 EntityBase 类中,有两种方法返回“真实”对象而不是代理,非常类似于此处描述的“As”方法:http: //sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded -继承.html
据我了解,这些方法本质上是返回由 NHibernate 代理对象包装的“真实”(域)对象。在我的情况下,有问题的方法称为“CastTo”和“AsOfType”。似乎是这些方法的类型限制导致了我的问题。
这是我的 EntityBase 类中的相关代码:
/// <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;
}
因此,当我在主应用程序解决方案中运行单元测试时,我会收到如下错误:
创建代理实例失败---> System.TypeLoadException:来自程序集'LocationProxyAssembly,Version = 0.0.0.0,Culture = Neutral,PublicKeyToken = null'的类型'LocationProxy'的方法'AsOfType'试图隐式覆盖具有较弱类型的方法参数约束。
因此,创建 NHibernate 代理的 Reflection.Emit 代码似乎在抱怨 AsOfType 和 CastTo 方法的类型约束。
所以我想我会稍微放松一下这些约束,而不是在类型约束中使用泛型,我会尝试只使用“Entity”作为约束(从 EntityBase 派生的类)。所以我尝试了以下方法(是的,我知道这两种方法本质上是做同样的事情,但我只是试图保留 EntityBase 类的接口以避免破坏对这些方法的所有调用):
[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;
}
所以现在测试以不同的方式失败了。我有一个看起来像这样的单元测试:
/// <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);
}
}
现在运行单元测试时抛出的异常是:
测试方法 MainSolution.Data.Tests.EntityProxyTests.TestEntityProxyCastTo 抛出异常:System.InvalidOperationException:不能对 ContainsGenericParameters 为 true 的类型或方法执行后期绑定操作。
所以这似乎仍然是一些 Reflection.Emit 错误,当 NHibernate 尝试生成代理时就会发生这种错误。很明显,NHibernate 现在生成其代理的方式与我们编写 EntityBase 类的方式不兼容。当我们使用 NHibernate.ByteCode.Castle.dll 生成代理时,它曾经工作正常,但显然对这些方法的类型约束不满意。
现在,我看到过这样的帖子( Nhibernate:获取真实实体类而不是代理类 ),这表明我们应该只“取消代理”该类以获取底层“真实”对象。但这意味着更改这些方法的签名(可能),中断对“CastTo”和“AsOfType”方法等的所有调用。据我了解,我必须获得一个 UnitOfWork,从中获取当前会话,然后执行“Unproxy”来获取底层对象,而使用当前代码,我们所要做的基本上就是返回“this as T”,它就可以工作了。我想也许我可以在调用代码中获取当前会话,然后将其传递给 EntityBase 上的某个方法,
所以问题是:a)我做错了什么,b)在 3.3. NHibernate 的版本,以及 c) 有没有办法在我的 EntityBase 类上保留“CastTo”和“AsOfType”方法的现有签名,同时仍然让 NHibernate 正确生成其代理而不抱怨这些方法的类型约束?
任何帮助表示赞赏,非常感谢。