.net lambda表达式合并
事情的起因是公司一个小伙子问了我个问题 “海哥,来帮我看下这段代码怎么不行”
Func<Report,bool> nameFilter = x=>x.Name == "test";
DbContext.Report.Where(x=>x.State==1 && nameFilter(x));
我一看,好家伙,这么骚的代码都能让你想出来,正常情况下用Linq To Object是可以这么操作的,但是EF的IQueryable
查询是不能这么操作的。
Linq To Object是直接执行表达式,他就是个委托方法,里面嵌套多少层委托和方法都是能直接执行的
IQueryable
并不会执行表达式和方法,是把表达式转换为对应的Sql语句来执行,解析到nameFilter的时候他就懵逼了,这是啥玩意儿啊,sql里面没有这种东西啊,他就转换不了了。
小伙子知道后明细很失望,那不能啊,也不是我想显摆我的技术,就是想让小伙子能继续他的骚操作,给他来点海克斯科技与狠活。
解决方案:
//表达式
Func<Report,bool> nameFilter = x=>x.Name == "test";
Func<Report,bool> stateFilter = x=>x.State==1;
//合并为
Func<Report,bool> whereFilter = x=>x.Name == "test" && x.State==1;
//调用
DbContext.Report.Where(whereFilter);
完美解决
那怎么合并,当然得自己构造一个新的表达式,构造表达式需要用到Expression
类,如果没有用过这个类,可以按照下面的方式来调试看看一个表达式转换为表达式树是怎么样的。
TestExpression(x=>x.Name == "test",x=>x.State==1);
public static void TestExpression(Expression<Func<Report, bool>> left,Expression<Func<Report, bool>> right)
{
//调试查看expression对象
var bodyLeft = left.Body;//这个就是x.Name == "test"
var bodyRight = right.Body;//这个就是x.State==1
}
好,这里我们能获取到表达式的Body,然后使用Expression
类能很好的合并两个表达式的body
var andAlso = Expression.AndAlso(bodyLeft ,bodyRight);//x.Name == "test" && x.State==1
这样还不行,这两个表达式是两个不同的委托对象,他们的参数x也是两个不同的对象,合并了又没完全合并
这就需要用到ExpressionVisitor
类来递归表达式树,把两个表达式的参数替换为同一个参数。
/// <summary>
/// 替换表达式参数
/// </summary>
public class ReplaceExpressionVisitor : ExpressionVisitor
{
private Expression _leftParameter;
public ReplaceExpressionVisitor(Expression leftParameter)
{
_leftParameter= leftParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _leftParameter;
}
}
最终
TestExpression(x=>x.Name == "test",x=>x.State==1);
public static void TestExpression(Expression<Func<Report, bool>> left,Expression<Func<Report, bool>> right)
{
//调试查看expression对象
var bodyLeft = left.Body;//这个就是x.Name == "test"
var bodyRight = right.Body;//这个就是x.State==1
var leftParameter = left.Parameters[0];
//表达式递归访问
var visitor =new ReplaceExpressionVisitor(leftParameter);
//替换参数
bodyRight = visitor.Visit(bodyRight);
//合并表达式
var expression = Expression.AndAlso(bodyLeft , bodyRight);
//构建表达式
var whereExpression= Expression.Lambda<Func<Report, bool>>(expression , left.Parameters);
//编译表达式
var whereFilter = whereExpression.Compile();
//使用
DbContext.Report.Where(whereFilter);
}
正想给小老弟显摆一下的时候,他又去写其他骚代码了
骚不过骚不过,完善一下列子,下面是完整的代码
小嫩手不想动的小伙伴可以直接nuget上查找DynamicExpression.Core
,直接使用
更多源码看本人github
/// <summary>
/// 替换表达式参数
/// </summary>
public class ReplaceExpressionVisitor : ExpressionVisitor
{
private Dictionary<Expression, Expression> _parameters;
public ReplaceExpressionVisitor(Dictionary<Expression,Expression> parameters)
{
_parameters = parameters;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_parameters.TryGetValue(node, out Expression _newValue))
{
return _newValue;
}
return base.Visit(node);
}
}
/// <summary>
/// 表达式扩展
/// </summary>
public static class ExpressionExtension
{
/// <summary>
/// 使用AndAlso合并表达式
/// </summary>
/// <param name="exprs"></param>
/// <returns></returns>
public static Expression<T> AndAlso<T>(this IList<Expression<T>> exprs)
{
if (exprs.Count == 0) return null;
if (exprs.Count == 1) return exprs[0];
var leftExpr = exprs[0];
var left = leftExpr.Body;
for (int i = 1; i < exprs.Count; i++)
{
var expr = exprs[i];
var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
var right = visitor.Visit(expr.Body);
left = Expression.AndAlso(left, right);
}
return Expression.Lambda<T>(left, leftExpr.Parameters);
}
/// <summary>
/// 使用AndAlso合并表达式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns>left AndAlso right</returns>
public static Expression<T> AndAlso<T>(this Expression<T> left, Expression<T> right)
{
return AndAlso(new List<Expression<T>>() { left, right });
}
/// <summary>
/// 使用OrElse合并表达式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exprs"></param>
/// <returns></returns>
public static Expression<T> OrElse<T>(this IList<Expression<T>> exprs)
{
if (exprs.Count == 0) return null;
if (exprs.Count == 1) return exprs[0];
var leftExpr = exprs[0];
var left = leftExpr.Body;
for (int i = 1; i < exprs.Count; i++)
{
var expr = exprs[i];
var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
var right = visitor.Visit(expr.Body);
left = Expression.OrElse(left, right);
}
return Expression.Lambda<T>(left, leftExpr.Parameters);
}
/// <summary>
/// 使用OrElse合并表达式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns>left OrElse right</returns>
public static Expression<T> OrElse<T>(this Expression<T> left, Expression<T> right)
{
return OrElse(new List<Expression<T>>() { left, right });
}
/// <summary>
/// 构建visitor
/// </summary>
/// <param name="oldParameters"></param>
/// <param name="newParameters"></param>
/// <returns></returns>
private static ReplaceExpressionVisitor GetReplaceExpressionVisitor(ReadOnlyCollection<ParameterExpression> oldParameters, ReadOnlyCollection<ParameterExpression> newParameters)
{
Dictionary<Expression, Expression> dic = new Dictionary<Expression, Expression>();
for (int i = 0; i < oldParameters.Count; i++)
{
dic.Add(oldParameters[i],newParameters[i]);
}
return new ReplaceExpressionVisitor(dic);
}
}
使用
string connectString = "Data Source=.;Initial Catalog=RportTest;Integrated Security=True";
var optionsBuilder = new DbContextOptionsBuilder<TestContext>();
optionsBuilder.UseSqlServer(connectString);
using (TestContext ctx = new TestContext(optionsBuilder.Options))
{
Expression<Func<ReportData, bool>> epxr1 = report => report.ID == 2023;
Expression<Func<ReportData, bool>> epxr2 = report => report.Name == "test1";
var epxr3 = new List<Expression<Func<ReportData, bool>>>() { epxr1, epxr2 };
var andPredicate = epxr3.AndAlso();
var andQuery = ctx.ReportData.Where(andPredicate);
string andSql = andQuery.ToQueryString();
var andResult = andQuery.ToList();
var orPredicate = epxr3.OrElse();
var orQuery = ctx.ReportData.Where(orPredicate);
string orSql = orQuery.ToQueryString();
var orResult = orQuery.ToList();
}
.net lambda表达式合并的更多相关文章
- 合并两个 Lambda 表达式
概述 在开发工作中,有些时候需要对一些增删改查进行封装(用 Lambda 表达式来筛选数据),但是又有一部分条件总是相同的,对于相同的部分可以直接写到方法里,而不同的部分作为参数传进去. 定义扩展方法 ...
- Util应用程序框架公共操作类(九):Lambda表达式扩展
上一篇对Lambda表达式公共操作类进行了一些增强,本篇使用扩展方法对Lambda表达式进行扩展. 修改Util项目的Extensions.Expression.cs文件,代码如下. using Sy ...
- Java 8特性探究(1):通往lambda之路与 lambda表达式10个示例
本文由 ImportNew 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的ja ...
- C#中的委托,匿名方法和Lambda表达式
简介 在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆.我想下面的代码能证实这点.下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=.答案是6个Firs ...
- 写的非常好的文章 C#中的委托,匿名方法和Lambda表达式
简介 在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆.我想下面的代码能证实这点.下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个Fir ...
- Java8特性详解 lambda表达式 Stream
1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正 ...
- Java 8 Lambda表达式10个示例【存】
PS:不能完全参考文章的代码,请参考这个文件http://files.cnblogs.com/files/AIThink/Test01.zip 在Java 8之前,如果想将行为传入函数,仅有的选择就是 ...
- (转)C#中的委托,匿名方法和Lambda表达式
简介 在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆.我想下面的代码能证实这点.下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个Fir ...
- 初探Lambda表达式/Java多核编程【2】并行与组合行为
今天又翻了一下书的目录,第一章在这之后就结束了.也就是说,这本书所涉及到的新的知识已经全部点到了. 书的其余部分就是对这几个概念做一些基础知识的补充以及更深层次的实践. 最后两个小节的内容较少,所以合 ...
随机推荐
- 我分析30w条数据后发现,西安新房公摊最低的竟是这里?
前两天一个邻居发出了灵魂质问:"为什么我买的180平和你的169平看上去一样大?" "因为咱俩的套内面积都是138平......" 我们去看房子,比较不同楼盘的 ...
- NC20242 [SCOI2005]最大子矩阵
题目链接 题目 题目描述 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大. 注意:选出的k个子矩阵 不能相互重叠. 输入描述 第一行为n,m,k(1 ≤ n ≤ 100 ...
- 在 Linux 安装 Java 的流程
前言 安装流程一共为 4 个步骤,分为下载.解压.配置.检查. 下载 Oracle 官网下载 JDK. 解压 上传至 Linux 中(可使用宝塔面板上传),解压安装包: ubuntu@VM-0-6-u ...
- Python小游戏——外星人入侵(保姆级教程)第一章 01创建Pygame窗口 02创建设置类Setting()
系列文章目录 第一章:武装飞船 01:创建Pygame窗口以及响应用户输入 02:创建设置类Setting() 一.前期准备 1.语言版本 Python3.9.0 2.编译器 Pycharm2022 ...
- LOJ#2014「SCOI2016」萌萌哒(倍增,并查集优化连边)
题面 点此看题 题意很明白,就不转述了吧. 题解 题目相当于告诉了我们若干等量关系,每个限制 l 1 , r 1 , l 2 , r 2 \tt l_1,r_1,l_2,r_2 l1,r1,l2 ...
- 【java】学习路线7-继承、super方法、重写、重载
/*继承-java只有单继承如果你创建了很多个class,但是之间有很多相同的成员变量和成员方法,修改的时候又要多处修改好麻烦,此时就可以创建多一个类来存储这些重复的东西,统一管理.相当方便.*//* ...
- KingbaseES V8R6集群管理运维案例之---repmgr standby switchover故障
案例说明: 在KingbaseES V8R6集群备库执行"repmgr standby switchover"时,切换失败,并且在执行过程中,伴随着"repmr stan ...
- linux中cd后自动 ls的设置
根据不同的shell设置不太一样.常见的有bash csh两种.可以用echo $SHELL来查询当前是哪一种. bash设置是在用户的home下打开.bashrc在里面加上如下: cd() { bu ...
- Go编译过程
一. Go编译流程 二.过程说明 1. 词法解析 读取Go源文件,将字符序列转换为符号(token)序列,比如将":="转换为_Define 代码中的标识符.关键字.运算符和分隔符 ...
- Java 函数式编程
由 JS 转 Java,写惯了 React,习惯了函数式,因此转 Java 时也是先学函数式. 语法糖「Syntactic Sugar」 起初,Java 的函数式看起来是匿名类的一个语法糖. Stre ...