整个周末都在摸不着头脑。
背景:项目是MVC3,C#4,FluentNHibernate(1.2.0.712),nHibernate(3.1.0.4)和StructureMap(2.6.3.0),都是通过NuGet安装的。这是一个我没有创建的大型代码库,我只是想为客户端添加一些功能......
现有的IPreInsertEventListener设置“审核”信息(CreatedDate,CreatedBy等),其中CreatedBy是通过某个IOC伏都教从HttpSessionState中提取的应用程序用户实体。这是预先存在的并且正在工作......
public class AuditEventListener : IPreInsertEventListener
{
public bool OnPreInsert(PreInsertEvent eventItem)
{
if (eventItem.Entity is IAudit)
{
IAudit auditObject = (IAudit)eventItem.Entity;
Store(eventItem.Persister, eventItem.State, "CreatedDate", DateTime.Now);
Store(eventItem.Persister, eventItem.State, "CreatedBy", GetCurrentUser());
Store(eventItem.Persister, eventItem.State, "LastModifiedDate", DateTime.Now);
Store(eventItem.Persister, eventItem.State, "LastModifiedBy", GetCurrentUser());
auditObject.CreatedBy = GetCurrentUser();
auditObject.CreatedDate = DateTime.Now;
auditObject.LastModifiedBy = GetCurrentUser();
auditObject.LastModifiedDate = DateTime.Now;
}
return false;
}
private Model.WebUser GetCurrentUser()
{
return ObjectFactory.GetInstance<ICurrentUser>().Get();
}
}
GetCurrentUser()函数是对一个服务的StructureMap ObjectFactory调用的包装器,该服务在HttpSessionState中查找当前登录的用户。它基本上是这样做的:
if(HttpContext.Current != null)
return WebUser.GetBy(wu => wu.EmailAddress == HttpContext.Current.Session["Email"].ToString();
else
return null;
Store函数是样板代码,它通过Persister中的名称设置eventItem.State数组中的值。省略,因为我不认为这是问题的一部分。
我添加了第二个IPreInsertEventListener,用于创建一个完整的表“历史记录”日志,基于nhibernate.evers(由于版本问题我不能在这里使用)。它基本上检查标记正在保存的实体的属性,如果找到它,则将当前值和旧值复制到关联的“HistoryModel”并保存。
public class HistoryEventListener : IPreInsertEventListener
{
public bool OnPreInsert(PreInsertEvent eventItem)
{
var model = TryGetHistoryModel(eventItem.Entity);
if (model != null)
{
MapToHistory(eventItem.Entity, model);
session.Save(model);
}
return false;
}
private static IHistoryModel TryGetHistoryModel(object entity)
{
var att = entity.GetAttribute<HistoryLogAttribute>();
if (att != null)
return ObjectFactory.GetInstance(att.HistoryModelType) as IHistoryModel;
else
return null;
}
}
MapToHistory函数基本上对传递给它的模型进行基于天体反射的映射,使用从eventItem.Persister获取的名称来定位属性名称。例如,eventItem.State的“ProcessingStatusId”属性将映射到model.NewProcessingStatusId。为简洁起见,我省略了它。假设正在制作并返回浅层副本。
侦听器通过StructureMap.Configuration.DSL.Registry连接,作为Fluently.Configure.BuildSessionFactory的一部分:
return Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008.ShowSql().ConnectionString(
connection => connection.FromConnectionStringWithKey("DefaultConnectionString")))
.Mappings(mapping => mapping.FluentMappings.AddFromAssemblyOf<SiteUser>())
.ExposeConfiguration(cfg =>
{
new SchemaUpdate(cfg).Execute(false, false);
cfg.EventListeners.PreInsertEventListeners = new NHibernate.Event.IPreInsertEventListener[] { new AuditEventListener(), new HistoryEventListener() };
})
.BuildSessionFactory();
现在所有的序言都不在考虑范围内,问题是:
如果我要创建并保存未使用HistoryAttribute标记的实体,则HistoryEventListener会跳过按原样处理它,并且一切正常。但是,如果保存了标有该属性的实体,则会发生以下过程:
AuditEventListener触发标记的实体,设置CreatedBy, 正确使用CreatedDate等,因为HttpContext.Current不为null。
HistoryEventListener触发,从标记中创建历史实体 实体并调用Session.Save。
AuditEventListener触发历史实体,设置CreatedDate,但CreatedBy设置为null,因为HttpContext.Current为null。
我应该明确指出我想在这里发生第3步,因此历史表收集CreatedDate和CreatedBy信息。我可以将它明确地添加到HistoryEventListener中,但之后我会重复自己。
我做了一点谷歌搜索,我很确定多个事件监听器本身不是问题 - 似乎是推荐的方式。
在事件监听器中保存新实体对象表单的行为是否会导致某种疯狂的上下文切换,从而导致当前的HttpContext被杀死?这是一个错误吗?
感谢。