You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 03:05:28