C# 使用DI向遗留代码中的NHibernate会话添加拦截器
所以,我维护的一些遗留代码中有一个bug。它会导致一些轻微的数据损坏,因此相当严重。我已经找到了根本原因,并制作了一个示例应用程序,可以可靠地重现该错误。我希望修复它时对现有应用程序的影响尽可能小,但我正在努力 缺陷存在于数据访问层。更具体地说,在如何将拦截器注入新的Nhibernate会话中。拦截器用于在保存或刷新时设置特定的实体属性。几乎在我们所有的实体上都可以找到LoggedInPersonID属性。所有实体都是使用数据库模式从CodeSmith模板生成的,因此LoggedInPersonID属性对应于数据库中几乎所有表上的一列。它与其他几个列和触发器一起用于跟踪哪个用户在数据库中创建和修改了记录。任何插入或更新数据的事务都需要提供LoggedInPersonID值,否则事务将失败 每当客户端需要新会话时,都会在SessionFactory(不是Nhibernate的SessionFactory,而是包装器)中调用OpenSession。下面的代码显示了SessionFactory包装类的相关部分:C# 使用DI向遗留代码中的NHibernate会话添加拦截器,c#,nhibernate,dependency-injection,unity-container,C#,Nhibernate,Dependency Injection,Unity Container,所以,我维护的一些遗留代码中有一个bug。它会导致一些轻微的数据损坏,因此相当严重。我已经找到了根本原因,并制作了一个示例应用程序,可以可靠地重现该错误。我希望修复它时对现有应用程序的影响尽可能小,但我正在努力 缺陷存在于数据访问层。更具体地说,在如何将拦截器注入新的Nhibernate会话中。拦截器用于在保存或刷新时设置特定的实体属性。几乎在我们所有的实体上都可以找到LoggedInPersonID属性。所有实体都是使用数据库模式从CodeSmith模板生成的,因此LoggedInPerson
public class SessionFactory
{
private ISessionFactory sessionFactory;
private SessionFactory()
{
Init();
}
public static SessionFactory Instance
{
get
{
return Nested.SessionFactory;
}
}
private static readonly object _lock = new object();
public ISession OpenSession()
{
lock (_lock)
{
var beforeInitEventArgs = new SessionFactoryOpenSessionEventArgs(null);
if (BeforeInit != null)
{
BeforeInit(this, beforeInitEventArgs);
}
ISession session;
if (beforeInitEventArgs.Interceptor != null
&& beforeInitEventArgs.Interceptor is IInterceptor)
{
session = sessionFactory.OpenSession(beforeInitEventArgs.Interceptor);
}
else
{
session = sessionFactory.OpenSession();
}
return session;
}
}
private void Init()
{
try
{
var configuration = new Configuration().Configure();
OnSessionFactoryConfiguring(configuration);
sessionFactory = configuration.BuildSessionFactory();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
while (ex.InnerException != null)
{
Console.Error.WriteLine(ex.Message);
ex = ex.InnerException;
}
throw;
}
}
private void OnSessionFactoryConfiguring(Configuration configuration)
{
if(SessionFactoryConfiguring != null)
{
SessionFactoryConfiguring(this, new SessionFactoryConfiguringEventArgs(configuration));
}
}
public static event EventHandler<SessionFactoryOpenSessionEventArgs> BeforeInit;
public static event EventHandler<SessionFactoryOpenSessionEventArgs> AfterInit;
public static event EventHandler<SessionFactoryConfiguringEventArgs> SessionFactoryConfiguring;
public class SessionFactoryConfiguringEventArgs : EventArgs
{
public Configuration Configuration { get; private set; }
public SessionFactoryConfiguringEventArgs(Configuration configuration)
{
Configuration = configuration;
}
}
public class SessionFactoryOpenSessionEventArgs : EventArgs
{
private NHibernate.ISession session;
public SessionFactoryOpenSessionEventArgs(NHibernate.ISession session)
{
this.session = session;
}
public NHibernate.ISession Session
{
get
{
return this.session;
}
}
public NHibernate.IInterceptor Interceptor
{
get;
set;
}
}
/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
internal static readonly SessionFactory SessionFactory;
static Nested()
{
try
{
SessionFactory = new SessionFactory();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw;
}
}
}
}
公共类SessionFactory
{
私人ISessionFactory sessionFactory;
私有会话工厂()
{
Init();
}
公共静态SessionFactory实例
{
得到
{
返回Nested.SessionFactory;
}
}
私有静态只读对象_lock=new object();
公共会话OpenSession()
{
锁
{
var beforeInitEventArgs=new SessionFactoryOpenSessionEventArgs(null);
if(BeforeInit!=null)
{
BeforeInit(此,beforeInitEventArgs);
}
闭会期间会议;
if(beforeInitEventArgs.Interceptor!=null
&&beforeInitEventArgs.Interceptor是IInterceptor)
{
session=sessionFactory.OpenSession(beforeInitEventArgs.Interceptor);
}
其他的
{
session=sessionFactory.OpenSession();
}
返回会议;
}
}
私有void Init()
{
尝试
{
var配置=新配置().Configure();
OnSessionFactoryConfiguration(配置);
sessionFactory=configuration.BuildSessionFactory();
}
捕获(例外情况除外)
{
Console.Error.WriteLine(例如消息);
while(例如InnerException!=null)
{
Console.Error.WriteLine(例如消息);
ex=ex.InnerException;
}
投掷;
}
}
私有void onSessionFactoryConfiguration(配置)
{
if(SessionFactoryConfiguring!=null)
{
SessionFactoryConfiguring(新的SessionFactoryConfiguringEventArgs(配置));
}
}
公共静态事件EventHandler BeforeInit;
公共静态事件EventHandler AfterInit;
公共静态事件处理程序SessionFactoryConfiguring;
公共类SessionFactoryConfiguringEventArgs:EventArgs
{
公共配置{get;private set;}
public SessionFactoryConfiguringEventArgs(配置)
{
配置=配置;
}
}
公共类SessionFactoryOpenSessionEventArgs:EventArgs
{
非公开会议;
public SessionFactoryOpenSessionEventArgs(NHibernate.iseSession会话)
{
this.session=会话;
}
公开会议
{
得到
{
返回此会话;
}
}
公共NHibernate.i侦听器拦截器
{
得到;
设置
}
}
///
///协助确保线程安全、懒惰的单例
///
私有类嵌套
{
内部静态只读SessionFactory SessionFactory;
静态嵌套()
{
尝试
{
SessionFactory=新SessionFactory();
}
捕获(例外情况除外)
{
Console.Error.WriteLine(ex);
投掷;
}
}
}
}
拦截器是通过BeforeInit事件注入的。以下是拦截器的实现:
public class LoggedInPersonIDInterceptor : NHibernate.EmptyInterceptor
{
private int? loggedInPersonID
{
get
{
return this.loggedInPersonIDProvider();
}
}
private Func<int?> loggedInPersonIDProvider;
public LoggedInPersonIDInterceptor(Func<int?> loggedInPersonIDProvider)
{
SetProvider(loggedInPersonIDProvider);
}
public void SetProvider(Func<int?> provider)
{
loggedInPersonIDProvider = provider;
}
public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState,
string[] propertyNames, NHibernate.Type.IType[] types)
{
return SetLoggedInPersonID(currentState, propertyNames);
}
public override bool OnSave(object entity, object id, object[] currentState,
string[] propertyNames, NHibernate.Type.IType[] types)
{
return SetLoggedInPersonID(currentState, propertyNames);
}
protected bool SetLoggedInPersonID(object[] currentState, string[] propertyNames)
{
int max = propertyNames.Length;
var lipid = loggedInPersonID;
for (int i = 0; i < max; i++)
{
if (propertyNames[i].ToLower() == "loggedinpersonid" && currentState[i] == null && lipid.HasValue)
{
currentState[i] = lipid;
return true;
}
}
return false;
}
}
公共类LoggedPersonidInterceptor:NHibernate.EmptyInterceptor
{
私有int?loggedInPersonID
{
得到
{
返回此.loggedInPersonIDProvider();
}
}
私有函数LoggedPersonidProvider;
公共LoggedInPersonIDInterceptor(Func loggedInPersonIDProvider)
{
SetProvider(LoggedPersonidProvider);
}
公共无效设置提供程序(Func提供程序)
{
LoggedIndPersonidProvider=提供者;
}
public override bool OnFlushDirty(对象实体、对象id、对象[]当前状态、对象[]先前状态、,
字符串[]属性名称,NHibernate.Type.IType[]类型)
{
返回SetLoggedInPersonID(当前状态、属性名称);
}
public override bool OnSave(对象实体、对象id、对象[]当前状态、,
字符串[]属性名称,NHibernate.Type.IType[]类型)
{
返回SetLoggedInPersonID(当前状态、属性名称);
}
受保护的bool SetLoggedInPersonID(对象[]当前状态,字符串[]属性名称)
{
int max=属性名称长度;
var lipid=对数人格;
对于(int i=0;ipublic static class LoggedInPersonIDInterceptorUtil
{
public static LoggedInPersonIDInterceptor Setup(Func<int?> loggedInPersonIDProvider)
{
var loggedInPersonIdInterceptor = new LoggedInPersonIDInterceptor(loggedInPersonIDProvider);
ShipRepDAL.ShipRepDAO.SessionFactory.BeforeInit += (s, args) =>
{
args.Interceptor = loggedInPersonIdInterceptor;
};
return loggedInPersonIdInterceptor;
}
}
}
using NHibernate;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace MyAmazingApplication
{
public class DependencyInjectionInterceptor : EmptyInterceptor
{
private readonly IServiceProvider _serviceProvider;
public DependencyInjectionInterceptor(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public T GetService<T>() => _serviceProvider.GetService<T>();
public T GetRequiredService<T>() => _serviceProvider.GetRequiredService<T>();
}
}
public void ConfigureServices(IServiceCollection services)
{
...
var cfg = new Configuration();
... // your config setup
cfg.SetListeners(NHibernate.Event.ListenerType.PreInsert, new[] { new AuditEventListener() });
cfg.SetListeners(NHibernate.Event.ListenerType.PreUpdate, new[] { new AuditEventListener() });
services.AddSingleton(cfg);
services.AddSingleton(s => s.GetRequiredService<Configuration>().BuildSessionFactory());
services.AddScoped(s => s.GetRequiredService<ISessionFactory>().WithOptions().Interceptor(new DependencyInjectionInterceptor(s)).OpenSession());
... // you other services setup
}
public class AuditEventListener : IPreUpdateEventListener, IPreInsertEventListener
{
public bool OnPreUpdate(PreUpdateEvent e)
{
var user = ((DependencyInjectionInterceptor)e.Session.Interceptor).GetService<ICurrentUser>();
if (e.Entity is IEntity)
UpdateAuditTrail(user, e.State, e.Persister.PropertyNames, (IEntity)e.Entity, false);
return false;
}
}