C# 实体更新记录:如何捕获和记录字段变化到日志

方案一:粗糙但可用
 

var changes = new List<string>();

void CompareAndAddChange<T>(string propertyName, T oldValue, T newValue, Func<T, string> descriptionFunc = null)
{
    if (!EqualityComparer<T>.Default.Equals(oldValue, newValue))
    {
        var oldValueStr = descriptionFunc != null ? descriptionFunc(oldValue) : oldValue.ToString();
        var newValueStr = descriptionFunc != null ? descriptionFunc(newValue) : newValue.ToString();
        changes.Add($"{propertyName}: [{oldValueStr}] => [{newValueStr}]");
    }
}

CompareAndAddChange("公司名称", cusOld.CompanyName, modelDto.CompanyName);
CompareAndAddChange("公司类型", cusOld.CompanyType, modelDto.CompanyType, x => x.GetDescription());
CompareAndAddChange("公司代码", cusOld.CompanyCode, modelDto.CompanyCode);
CompareAndAddChange("公司介绍", cusOld.Contents, modelDto.Contents);
CompareAndAddChange("会员等级", cusOld.MemberLevel, modelDto.MemberLevel, x => x.GetDescription());
CompareAndAddChange("会员类型", cusOld.MemberType, modelDto.MemberType, x => x.GetDescription());
CompareAndAddChange("业务员", cusOld.SaleId, modelDto.SaleId, async id =>
{
    var user = await _userRepository.GetWhereAsync(x => x.Id == id);
    return user?.NickName ?? id.ToString();
});
CompareAndAddChange("点数", cusOld.Ratio, modelDto.Ratio);
CompareAndAddChange("联系方式", cusOld.Phone, modelDto.Phone);
CompareAndAddChange("会员有效期", cusOld.MemberValidityPeriod, modelDto.MemberValidityPeriod);
CompareAndAddChange("最大API客户数", cusOld.MaxApiCusCount, modelDto.MaxApiCusCount);
CompareAndAddChange("最大渠道数", cusOld.MaxChannelCount, modelDto.MaxChannelCount);
CompareAndAddChange("最大供应商数", cusOld.MaxSupplierCount, modelDto.MaxSupplierCount);
CompareAndAddChange("备注", cusOld.Remark, modelDto.Remark);

var content = string.Join(",", changes);

这种优化方式做了以下改进:

  1. 单一函数处理比较和改变的添加:使用泛型方法 CompareAndAddChange 来处理每个属性的比较和变更添加,避免了重复的逻辑。

  2. 异步处理:在需要获取业务员信息时,通过异步方法从 _userRepository 中获取用户信息。

  3. 描述函数:对于特定属性(如枚举类型),提供了描述函数,用于获取更具描述性的字符串,提高了可读性。

这种方法不仅减少了代码量,还提高了可维护性和可扩展性,使得未来对属性的改动更易于处理和扩展。


方案二:优化更舒服、反射

var changes = new List<string>();

// 获取所有属性
var properties = typeof(ModelDto).GetProperties();

foreach (var property in properties)
{
    // 获取属性值
    var oldValue = property.GetValue(cusOld);
    var newValue = property.GetValue(modelDto);

    // 如果新旧值不相等
    if (!Equals(oldValue, newValue))
    {
        // 特殊处理 SaleId 属性
        if (property.Name == "SaleId")
        {
            var userNew = await _userRepository.GetWhereAsync(x => x.Id == (int)newValue);
            var userOld = await _userRepository.GetWhereAsync(x => x.Id == (int)oldValue);
            changes.Add($"业务员: [{userOld?.NickName}] => [{userNew?.NickName}]");
        }
        else
        {
            changes.Add($"{property.Name}: [{oldValue}] => [{newValue}]");
        }
    }
}

var content = string.Join(",", changes);

这种方法的优点包括:

  1. 自动化属性比较:利用反射获取 ModelDto 的所有属性,并比较旧值和新值。

  2. 特殊处理:对于特定属性(如 SaleId),可以根据需要进行特殊处理,例如异步获取用户信息。

  3. 简化代码:避免了手动编写每个属性的比较逻辑,使代码更加简洁和易于维护。

  4. 可扩展性:如果需要比较的属性增加或者需要额外的特殊处理,只需在相应的位置添加逻辑即可。

这种方法适合于需要自动化处理属性比较和生成变更日志的场景,能够有效地提高代码的复用性和可维护性。

方案三:进阶加描述

如果你想要在使用反射时获取属性的自定义备注或描述,你可以借助 C# 中的自定义属性来实现。以下是一个简单的示例,展示如何定义一个自定义属性,并在反射时使用它获取字段的备注:

首先,定义一个自定义属性类 FieldDescriptionAttribute

[AttributeUsage(AttributeTargets.Property)]
public class FieldDescriptionAttribute : Attribute
{
    public string Description { get; }

    public FieldDescriptionAttribute(string description)
    {
        Description = description;
    }
}

然后,在你的 ModelDto 类中使用这个自定义属性:

public class ModelDto
{
    [FieldDescription("公司名称")]
    public string CompanyName { get; set; }

    [FieldDescription("公司类型")]
    public CompanyType CompanyType { get; set; }

    [FieldDescription("公司代码")]
    public string CompanyCode { get; set; }

    // 其他属性省略
}

接下来,在生成变更内容时,使用这些自定义属性来获取字段的备注:

var changes = new List<string>();

var properties = typeof(ModelDto).GetProperties();

foreach (var property in properties)
{
    var oldValue = property.GetValue(cusOld);
    var newValue = property.GetValue(modelDto);

    if (!Equals(oldValue, newValue))
    {
        // 获取属性上的 FieldDescriptionAttribute
        var descriptionAttribute = (FieldDescriptionAttribute)property.GetCustomAttributes(typeof(FieldDescriptionAttribute), false).FirstOrDefault();

        // 如果找到了 FieldDescriptionAttribute,则使用其描述,否则使用属性名
        var propertyName = descriptionAttribute != null ? descriptionAttribute.Description : property.Name;

        // 特殊处理 SaleId 属性
        if (property.Name == "SaleId")
        {
            var userNew = await _userRepository.GetWhereAsync(x => x.Id == (int)newValue);
            var userOld = await _userRepository.GetWhereAsync(x => x.Id == (int)oldValue);
            changes.Add($"{propertyName}: [{userOld?.NickName}] => [{userNew?.NickName}]");
        }
        else
        {
            changes.Add($"{propertyName}: [{oldValue}] => [{newValue}]");
        }
    }
}

var content = string.Join(",", changes);

在上面的示例中,FieldDescriptionAttribute 允许你为每个属性指定一个自定义的描述。在遍历属性时,使用 GetCustomAttributes 方法获取属性上的自定义属性,并从中获取描述信息,以替代默认的属性名作为变更内容的一部分。

这种方法能够使你的代码更具可读性和可维护性,特别是在处理需要详细描述的属性时。

方案四:获取summary备注

/// <summary>
/// 备注
/// </summary>

如果你希望在生成变更内容时,使用属性的 XML 注释(summary),或者自定义的备注信息作为字段的描述,可以结合使用反射和 XML 注释读取来实现。以下是一个示例:

首先,假设你的 ModelDto 类定义如下:

public class ModelDto
{
    /// <summary>
    /// 公司名称
    /// </summary>
    public string CompanyName { get; set; }

    /// <summary>
    /// 公司类型
    /// </summary>
    public CompanyType CompanyType { get; set; }

    /// <summary>
    /// 公司代码
    /// </summary>
    public string CompanyCode { get; set; }

    // 其他属性省略
}

然后,可以编写一个方法来获取属性的备注信息,优先使用 XML 注释(summary),如果没有 XML 注释则使用自定义的备注信息:

public static string GetPropertyDescription(PropertyInfo property)
{
    // 尝试获取 XML 注释(summary)
    var xmlComment = property.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false).FirstOrDefault() as System.ComponentModel.DescriptionAttribute;
    if (xmlComment != null)
    {
        return xmlComment.Description;
    }

    // 如果没有 XML 注释,则使用属性名作为备注
    return property.Name;
}

最后,根据这个方法来生成变更内容:

var changes = new List<string>();

var properties = typeof(ModelDto).GetProperties();

foreach (var property in properties)
{
    var oldValue = property.GetValue(cusOld);
    var newValue = property.GetValue(modelDto);

    if (!Equals(oldValue, newValue))
    {
        var propertyName = GetPropertyDescription(property);

        // 特殊处理 SaleId 属性
        if (property.Name == "SaleId")
        {
            var userNew = await _userRepository.GetWhereAsync(x => x.Id == (int)newValue);
            var userOld = await _userRepository.GetWhereAsync(x => x.Id == (int)oldValue);
            changes.Add($"{propertyName}: [{userOld?.NickName}] => [{userNew?.NickName}]");
        }
        else
        {
            changes.Add($"{propertyName}: [{oldValue}] => [{newValue}]");
        }
    }
}

var content = string.Join(",", changes);

在上面的示例中,GetPropertyDescription 方法会尝试从 XML 注释(summary)中获取属性的描述信息,如果找不到 XML 注释,则默认使用属性的名称。这样可以根据属性的描述信息生成更具描述性的变更内容。

这种方法结合了反射和 XML 注释读取,能够使你的代码更加灵活和可维护,特别是在需要生成详细变更日志或报告时非常有用。

相关推荐

  1. postgresql触发器记录更新

    2024-07-17 13:38:03       51 阅读
  2. rust - 捕获全局panic并记录进程退出

    2024-07-17 13:38:03       47 阅读
  3. PHP记录

    2024-07-17 13:38:03       38 阅读
  4. 如何记录游戏开发过程中的

    2024-07-17 13:38:03       48 阅读
  5. logback整合rabbitmq实现消息记录

    2024-07-17 13:38:03       47 阅读
  6. Android-实现记录“异常闪退“

    2024-07-17 13:38:03       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 13:38:03       70 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 13:38:03       74 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 13:38:03       62 阅读
  4. Python语言-面向对象

    2024-07-17 13:38:03       72 阅读

热门阅读

  1. 从零开始!Jupyter Notebook的安装教程

    2024-07-17 13:38:03       23 阅读
  2. 数仓工具—Hive语法之替换函数和示例

    2024-07-17 13:38:03       19 阅读
  3. 油管吃播鼻祖被流量吞噬的半生

    2024-07-17 13:38:03       25 阅读
  4. ActiViz实战:ActiViz中的自己实现鼠标双击事件

    2024-07-17 13:38:03       29 阅读
  5. libwebrtc.a+exosip连接fS 环境部署tips

    2024-07-17 13:38:03       21 阅读
  6. NoSql选择题解

    2024-07-17 13:38:03       25 阅读
  7. Qt鼠标拖动线条组成的多边形移动

    2024-07-17 13:38:03       22 阅读