【问题标题】:NHibernate - Intercept changes of an entityNHibernate - 拦截实体的变化
【发布时间】:2021-09-05 18:30:55
【问题描述】:

我在 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 服务器表中添加一些信息,如果出现某些值(例如数据为空或 lastUser 为管理员)。

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

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

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

有没有办法在保存该实体时调用一个函数,允许我检查即将保存的值并最终将一些其他更改应用于数据库?

【问题讨论】:

    标签: c# sql-server nhibernate interceptor change-tracking


    【解决方案1】:

    是的。正如大卫所说,Eve​​ntListener 是你最好的选择。下面是我用审计字段值标记所有实体的示例。

    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 将更新发送到不同的数据库。

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

    【讨论】:

      【解决方案2】:

      documentation 看来,您不能基于类型应用拦截,但您必须在实现的方法中进行过滤:

      if (entity is IAuditable) {
                  
      }
      

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多