EF Core之自动历史记录

EF Core之自动历史记录

有的场景下需要记录特定表的增删改操作,以便追溯。传统的做法是在增删改的方法里同步做记录,很繁琐。在这里我们可以配合EF Core的DBContext做一个全局管控

  • 全局管控自然就要有固定的格式

    • 我们可以建一个空接ITrackable口来标明需要追踪的表:

      public interface ITrackable
      {
      }
      
    • 对于需要追踪的表,实现ITrackable作为标识:

      public class TableNeedTrack : ITrackable
      {
      }
      
    • 为历史记录表提供一个接口:

      public interface IHistory
      {
          /// <summary>
          /// 记录时间
          /// </summary>
          DateTime Hist_Created { get; set; }
          /// <summary>
          /// 记录行为
          /// </summary>
          string Hist_Action { get; set; }
          /// <summary>
          /// 记录对象id
          /// </summary>
          int? Hist_SourceId { get; set; }
      }
      
    • 对于历史记录表,实现IHistory,命名必须严格按照{TableNeedTrack}_Hist的格式,以便全局管控:

      public class TableNeedTrack_Hist : IHistory
      {
          /// <summary>
          /// Id
          /// </summary>
          public int Id { get; set; }
          /// <summary>
          /// 记录时间
          /// </summary>
          [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
          public DateTime Hist_Created { get; set; } = DateTime.Now;
          /// <summary>
          /// 记录行为
          /// </summary>
          public string Hist_Action { get; set; }
          /// <summary>
          /// 记录对象id
          /// </summary>
          public int? Hist_SourceId { get; set; }
      }
      
      • 历史记录有两种方式:1.将所有变化记录在一个Json字符串(判断真正变更的字符比较麻烦也损耗性能,不一定有必要);2.把原对象所有字段复制到历史记录表(本文选用此方法作为说明);
  • 做好以上准备后,就可以到DBContext里做全局管控了:

    //在SaveChanges里管控历史记录,这样就可以做到全自动处理
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        //获取原始历史记录
        var histEntries = OnBeforeSaving();
        //自动保存
        var result = base.SaveChanges(acceptAllChangesOnSuccess);
        //保存后再添加历史记录:有的信息需要数据库生成,比如id在保存前的话会得到-2147482647
        OnAfterSaving(histEntries);
        return result;
    }
    
    /// <summary>
    /// 保存前的操作:历史记录
    /// </summary>
    /// <returns>历史记录基础数据</returns>
    private List<HistoryEntry> OnBeforeSaving()
    {
        //预先保留需要做历史记录且有更改的Entry
        var histEntries = ChangeTracker.Entries<ITrackable>()
                                       .Where(e => e.State != EntityState.Detached && e.State != EntityState.Unchanged)
                                       .Select(e => new HistoryEntry { EntityEntry = e, State = e.State.ToString() })
                                       .ToList();
        return histEntries;
    }
    
    /// <summary>
    /// 保存后的操作:保存历史记录
    /// </summary>
    /// <param name="entries">历史记录基础数据</param>
    private void OnAfterSaving(List<HistoryEntry> entries)
    {
        if (entries.IsNullOrEmpty())
        {
            //这里很重要,确保只做正确的历史记录,保证历史记录的记录不能进行
            return;
        }
    
        foreach (var entry in entries)
        {
            var entity = (IEntity)entry.EntityEntry.Entity;
    
            Type entityType = entity.GetType();
            //历史记录必须和原对象同命名空间,名字=原对象+"_Hist"的格式
            var histTypeName = $"{entityType.Namespace}.{entityType.Name + "_Hist"}, {entityType.GetTypeInfo().Assembly.FullName}";
            Type histType = Type.GetType(histTypeName, false);
            if (histType != null)
            {
                var histEntity = (IHistory)Activator.CreateInstance(histType);
                if (histEntity != null)
                {
                    var histFields = histType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(k => k.Name);
                    var entityFields = entityType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    //这里过滤了Id(主键)
                    foreach (var field in entityFields.Where(af => histFields.ContainsKey(af.Name) && af.Name != "Id").ToList())
                    {
                        //将修改内容赋值给历史记录表同样的字段
                        var value = field.GetValue(entity);
                        histFields[field.Name].SetValue(histEntity, value);
                    }
    
                    histEntity.Hist_SourceId = entity.Id;
                    histEntity.Hist_Action = entry.State;
                    Add(histEntity);
                }
            }
        }
    
        SaveChanges();
    }
    
  • 这样就能够实现自动历史记录了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值