遇到很关注数据改变过程的场景,就会想把每次有改变的字段值记录下来,包括改变前及改变后。于是就想着找一个简单的方法来搞定。
最常见的比如前端调用修改接口传来DTO,我们根据主键先获取到对应的Entity,然后比较这两个对象来记录数据改变日志。
首先想到的就是反射后分别比较然后按指定格式收集改变了的值及属性信息。
代码如下:
/// <summary>
/// 比较2个实体属性名相同的属性,获取值发生改变了的字段信息
/// </summary>
/// <param name="t1">实体1</param>
/// <param name="t2">实体2</param>
/// <returns></returns>
public static List<Test> getChangedValues<T, A>(T t1, A t2) where T : class where A:class
{
var result = new List<Test>();
if (t1 == null || t2 == null)
{
return result;
}
var properties1 = t1.GetType().GetProperties();
var properties2 = t2.GetType().GetProperties();
foreach (var item in properties1)
{
var propertyInfo1 = properties1.FirstOrDefault(p => p.Name == item.Name);
var propertyInfo2 = properties2.FirstOrDefault(p => p.Name == item.Name);
if (propertyInfo1 == null | propertyInfo2 == null)
{
continue;
}
var v1 = propertyInfo1.GetValue(t1, null);
var v2 = propertyInfo2.GetValue(t2, null);
var entity = BuildEntity(item.Name, v1, v2);//构建信息
if (entity != null)
{
result.Add(entity);
}
}
return result;
}
但是问题来了,用户前端不会加载所有字段,这样没有加载的字段和Entity比较起来就全给记录到日志里去了,这就尴尬了。
怎么排除这些字段呢?
传一个List<string>来标识要比较哪些?NO NO NO 这样还得手动来拼这个List,而且如果实体字段删除了,编译器也未能感知。
那就加个表达式来表示吧。
代码如下:
/// <summary>
/// 比较2个实体指定的属性,获取值发生改变了的属性信息
/// </summary>
/// <param name="t1">实体1</param>
/// <param name="t2">实体2</param>
/// <param name="fun">要比较的属性列表</param>
/// <returns></returns>
public static List<Test> getChangedValues<T, A, B>(T t1, A t2, Expression<Func<T, B>> fun) where T : class where A:class where B:class
{
var result = new List<Test>();
if (t1 == null || t2 == null)
{
return result;
}
var properties = TypeDescriptor.GetProperties(fun.Compile().Invoke(t1));
var properties1 = t1.GetType().GetProperties();
var properties2 = t2.GetType().GetProperties();
foreach (PropertyDescriptor item in properties)
{
var propertyInfo1 = properties1.FirstOrDefault(p => p.Name == item.Name);
var propertyInfo2 = properties2.FirstOrDefault(p => p.Name == item.Name);
if (propertyInfo1 == null | propertyInfo2 == null)
{
continue;
}
var v1 = propertyInfo1.GetValue(t1, null);
var v2 = propertyInfo2.GetValue(t2, null);
var entity = BuildEntity(item.Name, v1, v2);//构建信息
if (entity != null)
{
result.Add(entity);
}
}
return result;
}
最后贴上BuildEntity方法实现:
private static Test BuildEntity(string name, object v1, object v2)
{
Test result = null;
if (v1 != null && !v1.Equals(v2))
{
result = new Test
{
Name = name,
Messge = "更改前:" + v1.ToString() + "更改后:" + v2.ToString()
};
}
else
{
if (v2 != null && !v2.Equals(v1))
{
result = new Test
{
Name = name,
Messge = "更改前:" + v1.ToString() + "更改后:" + v2.ToString()
};
}
}
return result;
}
这样使用起来就舒服安心多了:
public void Test()
{
var dd = new TestEntity()
{
F1 = "",
F2 = 1,
F3 = 2,
F4 = DateTime.Now,
F5 = 44,
F6 = false
};
var dd2 = new TestDto()
{
F1 = "2",
F2 = 11,
F3 = 23,
F4 = dd.F4,
F5 = 44,
F6 = false
};
//获取所有发生改变的属性
var c = Helper.getChangedValues(dd, dd2);
//获取指定几个属性里发生改变的属性
var c1 = Helper.getChangedValues(dd, dd2, p => new { p.F1, p.F2, p.F5 });
}
PS:对于字段类型不一致的情况,比如第一个类里F1字段是long 第二个类里F1字段是int,这里默认都是不相等的,如果你想要这种特殊处理请修改BuildEntity方法。