NHibernate升级使用具有类型约束的方法来破坏EntityBase

时间:2012-08-07 11:16:00

标签: nhibernate fluent-nhibernate

提前为长篇文章道歉。我希望有人可以提供帮助。

我被要求在ASP.NET应用程序中升级NHibernate(从2.1.2.4000到3.3.1.4000)和Fluent NHibernate(从1.1.0.685到1.3.0.0)。我是NHibernate的新手,但是已经花了几周时间研究这个,所以我有了一些了解。

例如,我知道新版本的NHibernate有一个内置的代理生成器,所以我删除了对旧的NHibnernate.ByteCode.Castle.dll的引用,这是我们以前使用的代理生成器,摆脱了来自nhibernate.config的对该文件的引用,目的是使用内置代理。

我已经成功地解决了各种各样的问题,但遇到了一个我坚持不懈的问题,我在网络上发现的任何内容似乎都与它完全对应。这是令人惊讶的,因为我认为任何进行过此类升级的人都会遇到这个问题,但也许这就是我们编写Entity Base类的方式。

有两个Visual Studio解决方案,其中一个是“Framework”解决方案,它包含EntityBase类,另一个是主要的应用程序解决方案,它使用Framework解决方案中的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;
    }

因此,当我在主应用程序解决方案中运行单元测试时,我遇到了这样的错误:

创建代理实例失败---&gt; System.TypeLoadException:方法'LocationProxy'上的方法'AsOfType'来自程序集'LocationProxyAssembly,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'试图隐式覆盖具有较弱类型参数约束的方法。

因此,创建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 个答案:

答案 0 :(得分:3)

有JIRA问题:

甚至还有一个拉动请求,遗憾的是它尚未拉动:

修改:提取拉取请求,因此修复了此特定问题。但还有另一个问题也影响.NET 2.0运行时:https://nhibernate.jira.com/browse/NH-3244

但是,仅使用“实体”作为约束的代码现在可以正常工作。