Asp.net mvc ASP.NET MVC中的NInject、nHibernate和审核
我正在开发一个继承的应用程序,它使用NInject和nHibernate作为ASP.NET MVC应用程序的一部分。目前,我正在研究修改的审计问题。每个实体都有ChangedOn/ChangedBy和CreatedOn/CreatedBy字段,这些字段映射到数据库列。但是,它们要么被错误的用户名填充,要么根本没有用户名。我认为这是因为它配置错误,但我对nHibernate和NInject了解不够,无法解决这个问题,所以我希望有人能提供帮助。下面是一些代码片段,希望能为应用程序提供足够的洞察力 创建会话工厂和会话: 正如您所看到的,会话工厂位于单例范围内。因此,我认为eventlistener和stamper也会在这个范围内实例化。这意味着当用户尚未登录时,母版中的用户名被设置为空字符串或未知。 我试图通过修改压模来弥补这个问题。它检查用户名是空的还是空的。如果这是真的,它将尝试查找活动用户,并用该用户名填充username属性:Asp.net mvc ASP.NET MVC中的NInject、nHibernate和审核,asp.net-mvc,nhibernate,ninject,auditing,Asp.net Mvc,Nhibernate,Ninject,Auditing,我正在开发一个继承的应用程序,它使用NInject和nHibernate作为ASP.NET MVC应用程序的一部分。目前,我正在研究修改的审计问题。每个实体都有ChangedOn/ChangedBy和CreatedOn/CreatedBy字段,这些字段映射到数据库列。但是,它们要么被错误的用户名填充,要么根本没有用户名。我认为这是因为它配置错误,但我对nHibernate和NInject了解不够,无法解决这个问题,所以我希望有人能提供帮助。下面是一些代码片段,希望能为应用程序提供足够的洞察力 创
private string GetUserName()
{
if (string.IsNullOrWhiteSpace(_userName))
{
var user = ServiceLocator.Resolve<User>();
if (user != null)
{
_userName = user.UserName;
}
}
return _userName;
}
但这会导致一个完全不同的用户名(也会登录到应用程序)被登录到数据库中。我猜这是因为它解决了错误的活动用户,即最后登录的用户,而不是启动事务的用户。有问题的部分如下:
Bind<ISessionFactory>().
.ToProvider(new SessionFactoryProvider())
.InSingletonScope();
Bind<IStamper>()
.ToProvider(new StamperProvider())
.InRequestScope();
后来:
public class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
// Unimportant lines omitted
var stamper = context.Kernel.Get<IStamper>();
return NHibernateHelper.CreateSessionFactory(connectionString, stamper);
}
}
public class StamperProvider : Provider<IStamper>
{
protected override IStamper CreateInstance(IContext context)
{
// Unimportant lines omitted
string name = /* whatever */
return new Stamper(name);
}
}
让我们分析一下代码中发生了什么:
ISessionFactory被绑定为单个实例。在整个流程的生命周期中,只有一个流程。这是相当典型的
ISessionFactory是用SessionFactoryProvider初始化的,SessionFactoryProvider立即出去获取IStamper的实例,并将其作为常量参数传递以初始化会话工厂
IStamper依次由StamperProvider初始化,StamperProvider使用设置为当前用户主体/标识的常量名称初始化Stamper类
这样做的最终结果是,只要进程处于活动状态,每个戳记都将被分配第一个登录的用户的名称。这甚至可能是匿名用户,这解释了为什么您会看到这么多空白条目
写这篇文章的人只得到了一半的正确答案。IStamper绑定到请求范围,但它被提供给一个单例,这意味着只会创建一个IStamper。幸运的是,Stamper没有任何资源或终结器,否则可能会导致大量ObjectDisposedException和其他奇怪的错误
有三种可能的解决方案:
建议-重写Stamper类以在每次调用时查找当前用户,而不是使用静态用户信息初始化。之后,Stamper类将不再接受任何构造函数参数。您可以在SingletonScope而不是InRequestScope中绑定IStamper
使用GetStamper方法创建一个抽象的IStamperFactory,并创建一个具体的StamperFactory,该StamperFactory通过包装IKernel实例来实现它。在单体示波器中将这些连接在一起。让您的混凝土工厂返回kernel.Get。修改会话工厂以接受并保留IStamperFactory而不是IStamper。每次需要盖章时,使用工厂获取一个新的IStamper实例
将ISessionFactory更改为在RequestScope中。不建议这样做,因为如果您不使用DB生成的标识,它将影响性能并可能会弄乱ID生成器,但它将解决您的审核问题
有问题的部分如下:
Bind<ISessionFactory>().
.ToProvider(new SessionFactoryProvider())
.InSingletonScope();
Bind<IStamper>()
.ToProvider(new StamperProvider())
.InRequestScope();
后来:
public class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
// Unimportant lines omitted
var stamper = context.Kernel.Get<IStamper>();
return NHibernateHelper.CreateSessionFactory(connectionString, stamper);
}
}
public class StamperProvider : Provider<IStamper>
{
protected override IStamper CreateInstance(IContext context)
{
// Unimportant lines omitted
string name = /* whatever */
return new Stamper(name);
}
}
让我们分析一下代码中发生了什么:
ISessionFactory被绑定为单个实例。在整个流程的生命周期中,只有一个流程。这是相当典型的
ISessionFactory是用SessionFactoryProvider初始化的,SessionFactoryProvider立即出去获取IStamper的实例,并将其作为常量参数传递以初始化会话工厂
IStamper依次由StamperProvider初始化,StamperProvider使用设置为当前用户主体/标识的常量名称初始化Stamper类
这样做的最终结果是,只要进程处于活动状态,每个戳记都将被分配第一个登录的用户的名称。这甚至可能是匿名用户,这解释了为什么您会看到这么多空白条目
写这篇文章的人只得到了一半的正确答案。IStamper绑定到请求范围,但它被提供给一个单例,这意味着只会创建一个IStamper。幸运的是,Stamper没有任何资源或终结器,否则可能会导致大量ObjectDisposedException和其他奇怪的错误
有三个po
可能的解决办法:
建议-重写Stamper类以在每次调用时查找当前用户,而不是使用静态用户信息初始化。之后,Stamper类将不再接受任何构造函数参数。您可以在SingletonScope而不是InRequestScope中绑定IStamper
使用GetStamper方法创建一个抽象的IStamperFactory,并创建一个具体的StamperFactory,该StamperFactory通过包装IKernel实例来实现它。在单体示波器中将这些连接在一起。让您的混凝土工厂返回kernel.Get。修改会话工厂以接受并保留IStamperFactory而不是IStamper。每次需要盖章时,使用工厂获取一个新的IStamper实例
将ISessionFactory更改为在RequestScope中。不建议这样做,因为如果您不使用DB生成的标识,它将影响性能并可能会弄乱ID生成器,但它将解决您的审核问题
Aaronaught,你的分析准确地描述了我的怀疑。然而,我发现还有第四种解决方案,它更简单、更直接。 我修改了sessionprovider,这样对OpenSession的调用将IIInterceptor的实例作为参数。事实证明,事件侦听器实际上不应该用于审计 AuditionInterceptor实现OnFlushDirty用于审核现有实体,实现OnSave用于审核新创建的实体。SessionProvider如下所示:
public class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
// Create session
System.Security.Principal.IPrincipal user = HttpContext.Current.User;
System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
string name = identity == null ? "" : identity.Name;
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession(new AuditInterceptor(name));
session.FlushMode = FlushMode.Commit;
return session;
}
}
Aaronaught,你的分析准确地描述了我的怀疑。然而,我发现还有第四种解决方案,它更简单、更直接。 我修改了sessionprovider,这样对OpenSession的调用将IIInterceptor的实例作为参数。事实证明,事件侦听器实际上不应该用于审计 AuditionInterceptor实现OnFlushDirty用于审核现有实体,实现OnSave用于审核新创建的实体。SessionProvider如下所示:
public class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
// Create session
System.Security.Principal.IPrincipal user = HttpContext.Current.User;
System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
string name = identity == null ? "" : identity.Name;
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession(new AuditInterceptor(name));
session.FlushMode = FlushMode.Commit;
return session;
}
}
UserProvider和StamperProvider中的代码是什么样子的?SessionFactory被配置为单例,因为创建SessionFactory的成本相对较高,而且您实际上只需要一个。ISession是一个NHibernate会话,它实际上与数据库对话,它是为每个RequestScope创建的,这意味着您可以为每个web请求获得一个新会话。我想知道UserProvider如何获取当前用户,StamperProvider如何将其附加到实体。Nathan,如果您使用session和session factory在代码块中向下滚动一点,您也可以看到这两个提供程序。我认为这很重要,但是代码块有点大,所以so的样式表用滚动条隐藏溢出。UserProvider和StamperProvider中的代码是什么样子的?SessionFactory被配置为单例,因为创建SessionFactory的成本相对较高,而且您实际上只需要一个。ISession是一个NHibernate会话,它实际上与数据库对话,它是为每个RequestScope创建的,这意味着您可以为每个web请求获得一个新会话。我想知道UserProvider如何获取当前用户,StamperProvider如何将其附加到实体。Nathan,如果您使用session和session factory在代码块中向下滚动一点,您也可以看到这两个提供程序。我认为这很重要,但是代码块有点大,所以so的样式表用滚动条隐藏溢出。这是一个非常糟糕的做法。依赖项不应该知道拦截器或任何其他DI/AOP管道的存在。如果您想采用这种方法,请使用Ninject.Extensions.Interception库(最好是Castle实现),并将拦截器添加到ISession绑定中,这可能是拦截器实际连接的位置。这是一种非常糟糕的做法。依赖项不应该知道拦截器或任何其他DI/AOP管道的存在。如果您想采用这种方法,请使用Ninject.Extensions.Interception库(最好是Castle实现),并向ISession绑定添加一个拦截器,这可能是拦截器实际连接的地方。您能帮我解决这个问题吗?在这个问题上你能帮我吗?