Angular+C# EF项目中如何识别Profile模型变更并记录活动日志?
对比Profile实例差异并生成操作日志的实现方案
我们已经通过var currentProfile = GetProfile(profile.Id);获取到修改前的Profile实例,同时拿到前端传来的修改后Profile对象,接下来可以通过两种方式对比差异并生成ProfileActivity记录存入数据库:
1. 手动字段对比(适合字段数量较少的场景)
直接逐个字段判断值是否不同,针对差异项创建日志记录,逻辑直观易维护:
// 假设已获取currentProfile、修改后的profile实例,以及当前操作用户user var activityLogs = new List<ProfileActivity>(); // 对比FirstName if (currentProfile.FirstName != profile.FirstName) { activityLogs.Add(new ProfileActivity { ProfileId = profile.Id, ActivityTypeId = 1, // 建议用枚举管理,比如1=修改操作 Description = "修改名字", OldValue = currentProfile.FirstName, NewValue = profile.FirstName, CreatedBy = user.Id, CreatedDate = DateTime.UtcNow }); } // 对比可空类型DateOfBirth if (currentProfile.DateOfBirth != profile.DateOfBirth) { activityLogs.Add(new ProfileActivity { ProfileId = profile.Id, ActivityTypeId = 1, Description = "修改出生日期", OldValue = currentProfile.DateOfBirth?.ToString("yyyy-MM-dd"), NewValue = profile.DateOfBirth?.ToString("yyyy-MM-dd"), CreatedBy = user.Id, CreatedDate = DateTime.UtcNow }); } // 对比可空类型RecruiterId if (currentProfile.RecruiterId != profile.RecruiterId) { activityLogs.Add(new ProfileActivity { ProfileId = profile.Id, ActivityTypeId = 1, Description = "修改招聘人员ID", OldValue = currentProfile.RecruiterId?.ToString(), NewValue = profile.RecruiterId?.ToString(), CreatedBy = user.Id, CreatedDate = DateTime.UtcNow }); } // 其他字段如MiddleName、LastName、VettingStatusId等按相同逻辑处理... // 批量插入日志 if (activityLogs.Any()) { _context.ProfileActivities.AddRange(activityLogs); await _context.SaveChangesAsync(); }
2. 反射自动对比(适合字段较多,避免重复代码)
通过反射遍历Profile的所有属性,自动对比值差异,减少重复代码:
// 通用差异对比方法 public List<ProfileActivity> GenerateProfileChangeLogs(Profile oldProfile, Profile newProfile, int operatorId, int activityTypeId) { var logs = new List<ProfileActivity>(); // 获取Profile的所有公开实例属性,排除主键Id var properties = typeof(Profile).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name != "Id") .ToList(); foreach (var prop in properties) { var oldValue = prop.GetValue(oldProfile); var newValue = prop.GetValue(newProfile); // 处理空值与值相等判断 bool isEqual = oldValue == null && newValue == null; if (!isEqual && oldValue != null) { isEqual = oldValue.Equals(newValue); } if (!isEqual) { // 格式化值为字符串,针对DateTime类型做特殊处理 string oldStr = FormatPropertyValue(oldValue, prop.PropertyType); string newStr = FormatPropertyValue(newValue, prop.PropertyType); logs.Add(new ProfileActivity { ProfileId = newProfile.Id, ActivityTypeId = activityTypeId, Description = $"修改{GetFriendlyName(prop.Name)}", OldValue = oldStr, NewValue = newStr, CreatedBy = operatorId, CreatedDate = DateTime.UtcNow }); } } return logs; } // 属性值格式化辅助方法 private string FormatPropertyValue(object value, Type propertyType) { if (value == null) return null; if (propertyType == typeof(DateTime?) || propertyType == typeof(DateTime)) { return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"); } return value.ToString(); } // 属性名转友好名称辅助方法 private string GetFriendlyName(string propName) { return propName switch { "FirstName" => "名字", "MiddleName" => "中间名", "LastName" => "姓氏", "DateOfBirth" => "出生日期", "VettingStatusId" => "审核状态ID", "RecruiterId" => "招聘人员ID", _ => propName // 默认返回原属性名 }; }
使用方式
在EditProfile方法中调用上述方法即可:
var currentProfile = await GetProfile(profile.Id); var changeLogs = GenerateProfileChangeLogs(currentProfile, profile, user.Id, 1); if (changeLogs.Any()) { _context.ProfileActivities.AddRange(changeLogs); } // 先更新Profile数据 _context.Profiles.Update(profile); await _context.SaveChangesAsync();
关键注意事项
- ActivityTypeId管理:建议定义枚举(如
ProfileActivityType)替代硬编码数字,例如Edit=1、Create=2,提升代码可读性。 - 敏感字段排除:如果Profile包含敏感属性(如密码),需要在反射对比时通过
Where条件排除,避免日志泄露敏感数据。 - 审计字段自动赋值:如果
BaseAuditModel包含创建人、创建时间等字段,可以通过EF的SaveChanges拦截器自动赋值,无需手动设置。
内容的提问来源于stack exchange,提问作者Jesus
相关产品推荐
相关产品推荐

