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的主要内容,如果未能解决你的问题,请参考以下文章

操作方法:从同一个表映射(NHibernate)具有不同业务逻辑的多个类?

Fluent NHibernate 主键约束命名约定

具有相同类型属性的 NHibernate 映射类

NHibernate“无法确定 X 的类型”错误

具有不满意类型约束的通用方法是隐藏扩展方法[重复]

具有自然键的流畅 NHibernate 引用实体