基于与 tucaz 的讨论,我在其核心相当简单的解决方案中提出了以下内容:
这个解决方案的核心是类NHibernateActivator
。它有两个重要目的:
- 创建对象的实例而不调用其构造函数。它
FormatterServices.GetUninitializedObject
用于此。
- 在 NHibernate 水合实例时防止触发“不变失败”异常。这是一个两步任务:在 NHibernate 开始补水之前禁用不变检查,并在 NHibernate 完成后重新启用不变检查。
第一部分可以在创建实例后直接执行。
第二部分是使用接口IPostLoadEventListener
。
类本身非常简单:
public class NHibernateActivator : INHibernateActivator, IPostLoadEventListener
{
public bool CanInstantiate(Type type)
{
return !type.IsAbstract && !type.IsInterface &&
!type.IsGenericTypeDefinition && !type.IsSealed;
}
public object Instantiate(Type type)
{
var instance = FormatterServices.GetUninitializedObject(type);
instance.DisableInvariantEvaluation();
return instance;
}
public void OnPostLoad(PostLoadEvent @event)
{
if (@event != null && @event.Entity != null)
@event.Entity.EnableInvariantEvaluation(true);
}
}
DisableInvariantEvaluation
并且EnableInvariantEvaluation
目前是使用反射来设置受保护字段的扩展方法。该字段防止检查不变量。此外EnableInvariantEvaluation
,如果它通过,将执行检查不变量的方法true
:
public static class CodeContractsExtensions
{
public static void DisableInvariantEvaluation(this object entity)
{
var evaluatingInvariantField = entity.GetType()
.GetField(
"$evaluatingInvariant$",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (evaluatingInvariantField == null)
return;
evaluatingInvariantField.SetValue(entity, true);
}
public static void EnableInvariantEvaluation(this object entity,
bool evaluateNow)
{
var evaluatingInvariantField = entity.GetType()
.GetField(
"$evaluatingInvariant$",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (evaluatingInvariantField == null)
return;
evaluatingInvariantField.SetValue(entity, false);
if (!evaluateNow)
return;
var invariantMethod = entity.GetType()
.GetMethod("$InvariantMethod$",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (invariantMethod == null)
return;
invariantMethod.Invoke(entity, new object[0]);
}
}
其余的是 NHibernate 管道:
- 我们需要实现一个使用我们的激活器的拦截器。
- 我们需要实现一个反射优化器来返回我们对
IInstantiationOptimizer
. 这个实现又再次使用我们的激活器。
- 我们需要实现一个使用我们的激活器的代理工厂。
- 我们需要实现
IProxyFactoryFactory
返回我们的自定义代理工厂。
- 我们需要创建一个自定义代理验证器,它不关心该类型是否具有默认构造函数。
- 我们需要实现一个返回我们的反射优化器和代理工厂工厂的字节码提供程序。
- NHibernateActivator 需要注册为使用Fluent NHibernate
config.AppendListeners(ListenerType.PostLoad, ...);
的监听器。ExposeConfiguration
- 我们的自定义字节码提供程序需要使用
Environment.BytecodeProvider
.
- 我们的自定义拦截器需要使用
config.Interceptor = ...;
.
当我有机会从所有这些中创建一个连贯的包并将其放在 github 上时,我将更新此答案。
此外,我想摆脱反射并创建一个代理类型,而不是直接访问受保护的 CodeContract 成员。
作为参考,以下博客文章有助于实现几个 NHibernate 接口:
不幸的是,目前这对于具有复合键的实体来说是失败的,因为反射优化器没有用于它们。这实际上是 NHibernate 中的一个错误,我在这里报告了它。