在做.NET Core开发工作的开发者们应该都知道,在EF Core之后,微软大大再也没有提供AddOrUpdate方法,但实际开发当中经常会有AddOrUpdate这样的需求,虽然能够自己根据业务进行定制化的实现,但有个通用的AddOrUpdate方法就会很实用了,而且,博主在曾经.NET Framework时代,也非常喜欢EF6的AddOrUpdate方法,经过了这么多年的不习惯,也在网络上寻找过各路方法,也不太行,虽然也有实现的,但不完美,那么今天,就再前人的坑的基础之上,给EF Core找回AddOrUpdate方法吧!
基本思路
为DbSet<T>类型添加一个扩展方法,然后我需要根据哪个属性进行数据的存在性判断,比如Id,或是手机号、身份证号等唯一键进行查存,由于查存的字段的不确定性,所以需要动态构建where的Expression,然后便可以判断传入的实体是需要新增还是更新操作。
逻辑实现
首先创建一个AddOrUpdate方法,为DbSet扩展:
public static void AddOrUpdate<T, TKey>(this DbSet<T> dbSet, Expression<Func<T, TKey>> keySelector, T entity)
其中参数keySelector是一个表达式树类型,决定传入的实体T entity根据哪个字段进行存在性判断。
如何根据keySelector表达式树来知道字段的名字是什么呢?通过表达式树编译,然后反射就知道了,通过调用keySelector的Compile方法,编译成一个Func类型的匿名函数,将entity传入func匿名函数进行调用,便可以得到entity的判重字段TKey的值,比如keySelector是一个根据字符串类型的Name查重的表达式,那么keySelector编译之后传入entity调用拿到的结果就是Name的值,代码如下:
Expression<Func<T, string>> keySelector=e=>e.Name;
var keyObject = keySelector.Compile()(entity); // 此时keyObject是一个string类型的值,该值就是entity的Name的值
但是在构造where表达式树之前,我们还需要做一件事情,就是封装Expression表达式树的参数访问,以便后续重用,在构造where表达式树,会用到表达式树的两种操作,分别是成员访问和创建新对象这两种操作,来生成我们需要的条件表达式树主体部分。比如讲上面的e=>e.Name表达式转换成e=>e.Name=="白火石"这样的形式。代码如下:
private static Expression ReplaceParameter(Expression oldExpression, ParameterExpression newParameter)
{
return oldExpression.NodeType switch
{