using Microsoft.EntityFrameworkCore.ChangeTracking; using YLErp; namespace BaseOUDAL { /// /// 数据变化追踪 /// public class DataChangeTracer { List _list; List<(DataTrace, EntityEntry)> _listAdded; readonly DBContextBase _dbcontext; readonly Dictionary _dicNew; //readonly Dictionary _dicOld; public DataChangeTracer(DBContextBase dbcontext) { _dbcontext = dbcontext ?? throw new ArgumentNullException(nameof(dbcontext)); _dicNew = new Dictionary(); //_dicOld = new Dictionary(); } /// /// 添加数据跟踪 /// public DataChangeTracer AddTraces(OptUserInfo optUserInfo) { var entityList = _dbcontext.ChangeTracker.Entries() .Where(p => p.State > EntityState.Unchanged && p.Entity is IDataTrace).ToArray(); if (entityList.Length < 1) { return this; } if (_list == null) { _list = new List(entityList.Length); } foreach (var entityEntry in entityList) { var dataTrace = GetDataTrace(entityEntry, optUserInfo); if (dataTrace == null) { continue; } _list.Add(dataTrace); if (dataTrace.TraceFlag == 1 && entityEntry.State == EntityState.Added) { if (_listAdded == null) { _listAdded = new List<(DataTrace, EntityEntry)>(); } _listAdded.Add((dataTrace, entityEntry)); } } return this; } /// /// 保存数据跟踪 /// public async void SaveTracesAsync() { if (_list == null || _list.Count < 1) { return; } DataTraceDbContext db = null; try { if (_listAdded != null) { foreach (var (trace, entry) in _listAdded) { trace.DataKeyId = GetKeyValue(entry); } } db = _dbcontext.GetDataTraceDbContext(); db.DataTrace.AddRange(_list); await db.SaveChangesAsync().ContinueWith(t => { if (t.Exception != null) { LogFactory.GetLogger().Error(t.Exception); } }); } catch (Exception ex) { LogFactory.GetLogger().Error(ex); } finally { db?.Dispose(); } } private DataTrace GetDataTrace(EntityEntry entityEntry, OptUserInfo optUserInfo) { if (entityEntry.Entity is IDataTraceIgnore traceIgnore && traceIgnore.IgnoreDataTrace()) { return null; } string dataChanges = GetDataChanges(entityEntry); var traceFlag = 0; var keyInfo = string.Empty; switch (entityEntry.State) { case EntityState.Added: traceFlag = 1; keyInfo = "新增"; break; case EntityState.Modified: traceFlag = 2; keyInfo = "修改"; if (string.IsNullOrEmpty(dataChanges)) { return null; } break; case EntityState.Deleted: traceFlag = 3; keyInfo = "删除"; break; } keyInfo += ((IDataTrace)entityEntry.Entity).GetDataTraceKeyInfo(); var keyId = string.Empty; if (entityEntry.Entity is IDataTraceV2 dataTraceV2) { keyId = dataTraceV2.GetDataTraceKeyId() ?? string.Empty; traceFlag = string.IsNullOrEmpty(keyId) || keyId == "0" ? 1 : traceFlag + 100; } else if (entityEntry.State != EntityState.Added) { keyId = GetKeyValue(entityEntry); } var user = optUserInfo ?? OptUserInfo.SystemUser; var entityType = entityEntry.Entity.GetType(); var tableName = entityType.Name; if (entityEntry.Entity is IDataTraceV3 traceV3) { var tableName2 = traceV3.GetDataTraceTableName(); if (!string.IsNullOrEmpty(tableName2)) { tableName = tableName2; } } return new DataTrace { TableName = tableName, DataState = entityEntry.State.ToString(), CreateTime = DateTime.Now, DataKeyId = keyId, DataKeyInfo = keyInfo, OptId = user.UserId, OptName = user.UserName, IpAddress = user.IpAddress, DataChanges = dataChanges, //OldValues = oldValues, TraceFlag = traceFlag }; } private static string GetKeyValue(EntityEntry entry) { var keyProps = entry.Metadata.FindPrimaryKey().Properties; if (keyProps == null || keyProps.Count < 1) { return string.Empty; } if (keyProps.Count > 0 && "id".Equals(keyProps[0].Name, StringComparison.OrdinalIgnoreCase)) { return entry.Property(keyProps[0].Name).CurrentValue?.ToString(); } return keyProps.ToDictionary(n => n.Name, m => entry.Property(m.Name).CurrentValue?.ToString()).ToJson(); } private string GetDataChanges(EntityEntry entry) { PropertyValues dbValues = null; if (entry.State != EntityState.Added) { dbValues = entry.GetDatabaseValues(); //等于null说明这个实体对象在数据库中无值但在实体上下文中做了变更(比如事务或附加) if (dbValues?.Properties == null || !dbValues.Properties.Any()) { return string.Empty; } } var dic = new Dictionary(); var properties = dbValues?.Properties ?? entry.CurrentValues.Properties; foreach (var prop in properties) { if (ExclueNames.Contains(prop.Name)) { continue; } switch (entry.State) { case EntityState.Added: { var newVal = entry.CurrentValues[prop]; if (newVal != null) { dic[prop.Name] = Convert.ToString(newVal); } } break; case EntityState.Deleted: { var oldVal = dbValues[prop]; if (oldVal != null) { dic[prop.Name] = Convert.ToString(oldVal); } } break; case EntityState.Modified: { var oldVal = dbValues[prop]; var newVal = entry.CurrentValues[prop]; var newValStr = Convert.ToString(newVal); var oldValStr = Convert.ToString(oldVal); if (!Equals(newValStr, oldValStr)) { dic[prop.Name] = newValStr; } } break; } } return dic.Count > 0 ? dic.ToJson() : string.Empty; } readonly static IEnumerable ExclueNames = new string[] { nameof(DBModelWithOperator.id), nameof(DBModelWithOperator.OptId), nameof(DBModelWithOperator.OptName), nameof(DBModelWithOperator.OptDate), nameof(SystemUser.UserToken), }; } }