我正在开发一个.NET MVC应用程序,它使用NHibernate和Fluent NHibernate进行映射。我有User和Role表/类,它们通过中间表UserRole映射。所以我的映射工作正常;我有用户映射:
HasManyToMany(user => user.Roles).Cascade.SaveUpdate()
因此,当我用一个新角色保存用户时,它会尝试插入一个新的UserRole行。大。
唯一的问题是我在UserRole表上有不可为空的审计列(如" UpdatedBy"和" UpdatedDate")。我尝试在UserRole构造函数中设置它们,但显然永远不会被调用,因为流畅的Nhibernate只是直接进入数据库并尝试插入没有审计列的新UserRoles。
所以我只是想知道是否有任何干净的方法来为Fluent Nhibernate中的这些列设置默认值。我可以关闭级联,并在保存用户时手动执行所有操作,但我想知道是否有更简洁明了的方法来执行此操作。
答案 0 :(得分:0)
使用像Envers这样的完整审核系统,或为自定义审核用例添加一些拦截器。
但是在关联表中添加其他属性会导致它难以作为纯关联表处理,只能通过多对多关系进行映射。
您可能更容易将其映射为中间实体(然后最好添加一个代理键),通过多对一关系链接到您的用户和角色实体。
拦截器会对任何更新或插入做出反应并相应地设置审计属性。对于实体和映射的审计属性,这很容易。对于您的情况,您必须修补发出的SQL。
这是我使用的,设置创建者(映射到某个用户实体而不是可空),创建日期(映射而不是可空),更新程序,更新日期(映射和空白)。 (改编自this NH reference example和this blog post。)
对于您的情况,您可能需要向SqlString OnPrepareStatement(SqlString sql)
和void OnCollectionUpdate(object collection, object key)
添加替换void OnCollectionRecreate(object collection, object key)
。
// Interceptor setting up audit properties.
[Serializable]
public class AuditInterceptor : NHibernate.EmptyInterceptor
{
public YourAppUser AppUser { get; set; }
public override bool OnFlushDirty(object entity,
object id,
object[] currentState,
object[] previousState,
string[] propertyNames,
NHibernate.Type.IType[] types)
{
var modified = false;
for (int i = 0; i < propertyNames.Length; i++)
{
switch (propertyNames[i])
{
case "UpdateDate":
currentState[i] = DateTimeOffset.Now;
modified = true;
break;
case "Updater":
currentState[i] = AppUser;
modified = true;
break;
}
}
return modified;
}
public override bool OnSave(object entity,
object id,
object[] state,
string[] propertyNames,
NHibernate.Type.IType[] types)
{
var modified = false;
for (int i = 0; i < propertyNames.Length; i++)
{
switch (propertyNames[i])
{
case "CreationDate":
state[i] = DateTimeOffset.Now;
modified = true;
break;
case "Creator":
state[i] = AppUser;
modified = true;
break;
}
}
return modified;
}
}
打开会话时注入拦截器实例:
yourNHibernateSessionFactory.OpenSession(yourInterceptorInstance);
在业务逻辑处理其工作之前,您应该设置当前用户的拦截器。在我的例子中,我在动作过滤器OnActionExecuting
方法中执行此操作,使用依赖项解析器获取我的拦截器,该拦截器具有每个http请求生命周期管理器。
此拦截器假定名为Creator
或Updater
的任何属性都是AppUser
属性,名为CreateDate
或UpdateDate
的任何属性都是{{1} }。例如,您可以通过检查datetimeoffset
是否实现某些自定义接口(某些检查类似于entity
来保证这样的假设,如参考示例所做的那样)。或者您也可以查看if (!(entity is IYourAuditableInterface)) return false;
参数。