ASP.NET MVC 中的 NInject、nHibernate 和审计 [英] NInject, nHibernate, and auditing in ASP.NET MVC

查看:27
本文介绍了ASP.NET MVC 中的 NInject、nHibernate 和审计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个继承的应用程序,它使用 NInject 和 nHibernate 作为 ASP.NET MVC (C#) 应用程序的一部分.目前,我正在研究修改审核的问题.每个实体都有 ChangedOn/ChangedBy 和 CreatedOn/CreatedBy 字段,它们映射到数据库列.但是,这些要么填充了错误的用户名,要么根本没有用户名.我认为这是因为它以错误的方式配置,但我对nHibernate和NInject的了解不够,无法解决问题,所以希望有人能提供帮助.下面是一些代码片段,希望能在应用程序中提供足够的洞察力.

I am working on an inherited application which makes use of NInject and nHibernate as part of an ASP.NET MVC (C#) application. Currently, I'm looking at a problem with the auditing of modifications. Each entity has ChangedOn/ChangedBy and CreatedOn/CreatedBy fields, which are mapped to database columns. However, these either get filled with the wrong username or no username at all. I think this is because it has been configured in the wrong way, but I don't know enough about nHibernate and NInject to solve the issue, so I hope someone can help. Below some code snippets to hopefully provide sufficient insight in the application.

创建会话工厂和会话:

public class NHibernateModule : NinjectModule
{
    public override void Load()
    {
        Bind<ISessionFactory>().ToProvider(new SessionFactoryProvider()).InSingletonScope();

        Bind<ISession>().ToProvider(new SessionProvider()).InRequestScope();
        Bind<INHibernateUnitOfWork>().To<NHibernateUnitOfWork>().InRequestScope();
        Bind<User>().ToProvider(new UserProvider()).InRequestScope();
        Bind<IStamper>().ToProvider(new StamperProvider()).InRequestScope();
    }
}

public class SessionProvider : Provider<ISession>
{
    protected override ISession CreateInstance(IContext context)
    {
        // Create session
        var sessionFactory = context.Kernel.Get<ISessionFactory>();            
        var session = sessionFactory.OpenSession();            
        session.FlushMode = FlushMode.Commit;

        return session;
    }
}

public class SessionFactoryProvider : Provider<ISessionFactory>
{
    protected override ISessionFactory CreateInstance(IContext context)
    {
        var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnectionString"].ToString();
        var stamper = context.Kernel.Get<IStamper>();

        return NHibernateHelper.CreateSessionFactory(connectionString, stamper);
    }
}

public class StamperProvider : Provider<IStamper>
{
    protected override IStamper CreateInstance(IContext context)
    {
        System.Security.Principal.IPrincipal user = HttpContext.Current.User;
        System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
        string name = identity == null ? "Unknown" : identity.Name;

        return new Stamper(name);
    }
}

public class UserProvider : Provider<User>
{
    protected override UserCreateInstance(IContext context)
    {
        var userRepos = context.Kernel.Get<IUserRepository>();

        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 user = userRepos.GetByName(name);
        return user;
    }
}

配置会话工厂:

public static ISessionFactory CreateSessionFactory(string connectionString, IStamper stamper)
    {
        // Info: http://wiki.fluentnhibernate.org/Fluent_configuration
        return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                    .ConnectionString(connectionString))
                .Mappings(m => 
                    {
                        m.FluentMappings
                            .Conventions.Add(PrimaryKey.Name.Is(x => "Id"))
                            .AddFromAssemblyOf<NHibernateHelper>();

                        m.HbmMappings.AddFromAssemblyOf<NHibernateHelper>();
                    })
                // Register 
                .ExposeConfiguration(c => {
                    c.EventListeners.PreInsertEventListeners = 
                        new IPreInsertEventListener[] { new EventListener(stamper) };
                    c.EventListeners.PreUpdateEventListeners =
                        new IPreUpdateEventListener[] { new EventListener(stamper) };
                })
                .BuildSessionFactory();
     }

来自事件监听器的片段:

public bool OnPreInsert(PreInsertEvent e)
{
    _stamper.Insert(e.Entity as IStampedEntity, e.State, e.Persister);
    return false;
}

如您所见,会话工厂处于单例范围内.因此 eventlistener 和 stamper 也在这个范围内被实例化(我认为).这意味着当用户尚未登录时,印模中的用户名将设置为空字符串或未知".我试图通过修改 Stamper 来弥补这个问题.它检查用户名是否为空或为空.如果这是真的,它会尝试找到活动用户,并用该用户的名称填充用户名属性:

As you can see the session factory is in a singleton scope. Therefore the eventlistener and stamper also get instantiated in this scope (I think). And this means that when the user is not yet logged in, the username in the stamper is set to an empty string or "Unknown". I tried to compensate for this problem, by modifying the Stamper. It checks if the username is null or empty. If this is true, it tries to find the active user, and fill the username-property with that user's name:

    private string GetUserName()
    {
        if (string.IsNullOrWhiteSpace(_userName))
        {
            var user = ServiceLocator.Resolve<User>();

            if (user != null)
            {
                _userName = user.UserName;
            }
        }

        return _userName;
    }

但这会导致一个完全不同的用户名,该用户名也登录到应用程序,登录到数据库中.我猜这是因为它解决了错误的活动用户,即最后登录的用户,而不是启动事务的用户.

But this results in a completely different user's name, which is also logged in to the application, being logged in the database. My guess this is because it resolves the wrong active user, being the last user logged in, instead of the user that started the transaction.

推荐答案

Aaronaught,你的分析准确地描述了我的怀疑.但是,我发现有第四种解决方案,恕我直言,它更简单、更直接.我修改了 sessionprovider,这样对 OpenSession 的调用将 IInterceptor 的一个实例作为参数.事实证明,事件侦听器实际上不应该用于审计(有点咆哮,但除此之外他也是对的,据法比奥说).

Aaronaught, you're analysis describes exactly what I suspected. However, I found there is a fourth solution which is easier and more straightforward IMHO. I modified the sessionprovider, such that the call to OpenSession takes an instance of IInterceptor as argument. As it turns out, the event listeners aren't actually supposed to be used for auditing (a bit of a rant, but other than that he is right, according to Fabio as well).

AuditInterceptor 实现了 OnFlushDirty(用于审计现有实体)和 OnSave(用于审计新创建的实体).SessionProvider 如下所示:

The AuditInterceptor implements OnFlushDirty (for auditing existing entities) and OnSave (for auditing newly created entities). The SessionProvider looks as below:

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;
    }
}

这篇关于ASP.NET MVC 中的 NInject、nHibernate 和审计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆