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();
}
-
这样就能够实现自动历史记录了
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)