NHibernate-拦截实体的更改



我在SQL服务器中有一个非常简单的表,它映射在C#POCO对象中,代码如下:

public class DataTable : Entity<Guid>
{
public virtual int itemId { get; set; }
public virtual string data { get; set; }
public virtual DateTime lastDate { get; set; }
public virtual string lastUser { get; set; }
}
public class DataTableMap : ClassMap<DataTable>
{
public DataTableMap()
{
Map(x => x.itemId);
Map(x => x.data);
Map(x => x.lastDate);
Map(x => x.lastUser);
}
}

我想使用Hibernate拦截器来跟踪更改,这样我就可以在另一个SQL server表中添加一些信息,如果出现某些值(例如,data为空或lastUser为Admin(。

我实现了一个简单的拦截器:

public class TrackingEntityInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
var persistentObject = entity as IPersistentObject;
if (persistentObject != null)
{
persistentObject.OnSave();
}
return false;
}
public override SqlString OnPrepareStatement(SqlString sql)
{
ApplicationBlocks.Logging.Log.Debug($"Executing sql: {sql.ToString()}");
return sql;
}
}

但我无法访问保存的实体的值,也无法仅在保存特定实体时激活拦截器。

有没有一种方法可以在保存实体时调用一个函数,让我检查即将保存的值,并最终将一些其他更改应用到DB?

是的。正如David所说,EventListener是您的最佳选择。下面是一个示例,我使用它为所有实体标记审计字段值。

namespace NHibernate.Extensions.EventListeners
{
public class EventListener : IPreInsertEventListener, IPreUpdateEventListener
{
private readonly IStamper _stamper;
public EventListener() : this(new Stamper())
{
}
public EventListener(IStamper stamper)
{
_stamper = stamper;
}
public bool OnPreInsert(PreInsertEvent @event)
{
_stamper.Insert(@event.Entity as IStampedEntity, @event.State, @event.Persister);
return false;
}
public Task<bool> OnPreInsertAsync(PreInsertEvent @event, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public bool OnPreUpdate(PreUpdateEvent @event)
{
_stamper.Update(@event.Entity as IStampedEntity, @event.OldState, @event.State, @event.Persister);
return false;
}
public Task<bool> OnPreUpdateAsync(PreUpdateEvent @event, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

然后压模

public class Stamper : IStamper
{
private const string CreateUser = "CreateUser";
private const string CreateDate = "CreateDate";
private const string LastUpdateUser = "LastUpdateUser";
private const string LastUpdateDate = "LastUpdateDate";
public void Insert(IStampedEntity entity, object[] state, IEntityPersister persister)
{
if (entity == null)
return;
SetCreate(entity, state, persister);
SetChange(entity, state, persister);
}
public void Update(IStampedEntity entity, object[] oldState, object[] state, IEntityPersister persister)
{
if (entity == null)
return;
SetChange(entity, state, persister);
}
private void SetCreate(IStampedEntity entity, object[] state, IEntityPersister persister)
{
entity.CreateUser = GetUserName();
SetState(persister, state, CreateUser, entity.CreateUser);
entity.CreateDate = DateTime.UtcNow;
SetState(persister, state, CreateDate, entity.CreateDate);
}
private void SetChange(IStampedEntity entity, object[] state, IEntityPersister persister)
{
entity.LastUpdateUser = GetUserName();
SetState(persister, state, LastUpdateUser, entity.LastUpdateUser);
entity.LastUpdateDate = DateTime.UtcNow;
SetState(persister, state, LastUpdateDate, entity.LastUpdateDate);
}
private void SetState(IEntityPersister persister, IList<object> state, string propertyName, object value)
{
var index = GetIndex(persister, propertyName);
if (index == -1)
return;
state[index] = value;
}
private int GetIndex(IEntityPersister persister, string propertyName)
{
return Array.IndexOf(persister.PropertyNames, propertyName);
}
private string GetUserName()
{
return HttpContext.Current != null
? HttpContext.Current.User.Identity.Name
: WindowsIdentity.GetCurrent().Name;
}
}

PreInsert将在更改刷新到数据库和事务中之前运行。您可能想使用PostInsert/PostUpdate将更新发送到其他数据库。

更新不同数据库的更好方案可能是使用服务总线通知订户进行了更改,然后让订户对外部数据库进行更改。

从文档中可以看出,您不能基于类型应用拦截,但必须在实现的方法中进行筛选:

if (entity is IAuditable) {

}

通过FlushDirty()方法的签名,您可以访问实体的先前和当前状态:

public override bool OnFlushDirty(object entity, 
object id, 
object[] currentState,
object[] previousState,
string[] propertyNames,
IType[] types)

这里需要考虑的另一件事是,事件监听器可以说是处理NHibernate中这方面行为的首选方式:

  • https://ayende.com/blog/3987/nhibernate-ipreupdateeventlistener-ipreinserteventlistener
  • NHibernate:侦听器和侦听器之间的区别

最新更新