【发布时间】:2011-08-17 09:31:39
【问题描述】:
我正在开发一个继承的应用程序,它使用 NInject 和 nHibernate 作为 ASP.NET MVC (C#) 应用程序的一部分。目前,我正在研究修改审计的问题。每个实体都有 ChangedOn/ChangedBy 和 CreatedOn/CreatedBy 字段,它们映射到数据库列。但是,这些要么填写错误的用户名,要么根本没有用户名。我认为这是因为它的配置方式错误,但是我对nHibernate和NInject的了解还不够,无法解决问题,所以希望有人能提供帮助。下面是一些代码 sn-ps,希望能在应用程序中提供足够的洞察力。
创建会话工厂和会话:
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 来弥补这个问题。它检查用户名是空还是空。如果这是真的,它会尝试找到活动用户,并用该用户的名称填充用户名属性:
private string GetUserName()
{
if (string.IsNullOrWhiteSpace(_userName))
{
var user = ServiceLocator.Resolve<User>();
if (user != null)
{
_userName = user.UserName;
}
}
return _userName;
}
但这会导致一个完全不同的用户名,该用户名也登录到应用程序,登录到数据库中。我猜这是因为它解决了错误的活动用户,即最后一个登录的用户,而不是启动事务的用户。
【问题讨论】:
-
UserProvider 和 StamperProvider 中的代码是什么样的? SessionFactory 被配置为单例,因为创建 SessionFactory 相对昂贵,而您实际上只需要一个。 ISession 是实际与数据库对话的 NHibernate 会话,它是为每个 RequestScope 创建的,这意味着您会为每个 Web 请求获得一个新会话。我想知道您的 UserProvider 如何获取当前用户以及 StamperProvider 如何将其附加到实体。
-
Nathan,如果您在带有会话和会话工厂的代码块中向下滚动一点,您也可以看到这两个提供程序。我认为这很重要,但是代码块有点大,所以 SO 的样式表用滚动条隐藏了溢出。
标签: asp.net-mvc nhibernate ninject auditing