NHibernate 升级使用具有类型约束的方法破坏 EntityBase
Posted
技术标签:
【中文标题】NHibernate 升级使用具有类型约束的方法破坏 EntityBase【英文标题】:NHibernate upgrade breaks EntityBase with methods having type constraints 【发布时间】:2012-08-07 11:16:00 【问题描述】:提前为这篇长文道歉。我希望有人可以提供帮助。
我被要求在 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-inheritance.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,版本=0.0.0.0,文化=中性,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: Get real entity class instead of proxied class),它建议我们应该只“取消代理”该类以获取底层的“真实”对象。但这意味着更改这些方法的签名(也许),中断对“CastTo”和“AsOfType”方法等的所有调用。据我了解,我必须获得一个 UnitOfWork,从中获取当前会话,然后执行“Unproxy”来获取底层对象,而使用当前代码,我们所要做的基本上就是返回“this as T”,它就可以工作了。我想也许我可以在调用代码中获取当前会话,然后将其传递给 EntityBase 上的某个方法,但这看起来很丑陋,而且做一些看起来应该更简单的事情会产生很多开销。
所以问题是:a)我做错了什么,b)鉴于 3.3. NHibernate 的版本,以及 c) 有没有办法在我的 EntityBase 类上保留“CastTo”和“AsOfType”方法的现有签名,同时仍然让 NHibernate 正确生成其代理而不抱怨这些方法的类型约束?
任何帮助表示赞赏,非常感谢。
【问题讨论】:
【参考方案1】:它存在 JIRA 问题:
https://nhibernate.jira.com/browse/NH-2726 https://nhibernate.jira.com/browse/NH-2819甚至还有一个拉取请求,遗憾的是它还没有被拉取:
https://github.com/nhibernate/nhibernate-core/pull/129编辑:拉取请求已被拉取,因此已修复此特定问题。但还有另一个问题也会影响 .NET 2.0 运行时:https://nhibernate.jira.com/browse/NH-3244
但是您的仅使用“实体”作为约束的代码现在应该可以工作了。
【讨论】:
是的,在我发布这个之后我也注意到了这个:nhibernate.jira.com/browse/NH-2698 顺便说一句,我们在这个项目中仍然使用 .Net 3.5 @user1518373 很奇怪,上次我尝试使用 .Net 2.0 运行时(由 .Net 3.5 项目使用)。 由于我是 NHibernate 的新手,我想知道 NHibernate 团队修复此类错误的周转时间可能是多少? (似乎这个已经存在了一段时间,或者,它已被修复,但可能在后续版本中再次被破坏?) OSS 之美:Hazzik 今天修复了 :-)以上是关于NHibernate 升级使用具有类型约束的方法破坏 EntityBase的主要内容,如果未能解决你的问题,请参考以下文章