提前为长篇文章道歉。我希望有人可以提供帮助。
我被要求在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正确生成代理而不抱怨这些方法的类型约束?
感谢任何帮助,非常感谢。
答案 0 :(得分:3)
有JIRA问题:
甚至还有一个拉动请求,遗憾的是它尚未拉动:
修改:提取拉取请求,因此修复了此特定问题。但还有另一个问题也影响.NET 2.0运行时:https://nhibernate.jira.com/browse/NH-3244
但是,仅使用“实体”作为约束的代码现在可以正常工作。