一、前言

本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧。

看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知道了。

表达式系列目录

C# 表达式树讲解(一)

C# 表达式树遍历(二)

C# 表达式树分页扩展(三)

C# 表达式树Lambda扩展(四)

二、Lambda扩展

这里先不忙解答上面的问题,我们先看下这样一个应用场景。

一个页面的请求,里面带有一些条件查询,请求类如下

public class ScoreRequest
{
public string CourseName { get; set; }
public string StudentName { get; set; }
}

要求查询与课程名称和学生名称匹配的数据

数据源我们就以上一例子的数据源

数据源类

public class ScoreClass
{
public string CourseName { get; set; }
public string StudentName { get; set; }
public decimal Score { get; set; }
}

添加数据

var datas = new List<ScoreClass>();
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生A",
Score = 60
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生B",
Score = 65
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生C",
Score = 70
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生D",
Score = 75
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生E",
Score = 80
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生F",
Score = 81
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生G",
Score = 82
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生H",
Score = 83
});
datas.Add(new ScoreClass
{
CourseName = "数学",
StudentName = "学生I",
Score = 84
});

好了现在我们就查询数据

var request = new ScoreRequest()
{
CourseName = "数",
StudentName = "H"
};
var resultDatas = datas.Where(e => e.CourseName.Contains(request.CourseName) && e.StudentName.Contains(request.StudentName))
.ToList();

如果查询对象里面CourseName和StudentName字段都有值得话,这样写没问题。如果没值,那就最后的数据,就不准确了。

如果是直接拼凑sql语句,我们可以用if(String.IsNullOrEmpty())来判断,但是现在判断了,怎么拼凑Lambda表达式呢?

所以就需要我们对Lambda表达式进行扩展,让他支持这种情况。那上面的问题,就不用再专门回答了吧!!!!

创建一个LambdaExtension的类,代码如下

public static class LambdaExtension
{
public static Expression<Func<T, bool>> True<T>() { return param => true; }
public static Expression<Func<T, bool>> False<T>() { return param => false; }
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
var secondBody = PFTParameterExtension.ReplaceParameters(map, second.Body);
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
} private class PFTParameterExtension : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> map; public PFTParameterExtension()
{ } public PFTParameterExtension(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
} /// <summary>
/// 替换参数
/// </summary>
/// <param name="map">The map.</param>
/// <param name="exp">The exp.</param>
/// <returns>Expression</returns>
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new PFTParameterExtension(map).Visit(exp);
} protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map != null && map.Count > 0 && map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
} } }

这里面私有化了一个表达式树访问器,他的作用主要是用来同步Lambda表达式里面的参数。

下面是调用方式

            var expression = LambdaExtension.True<ScoreClass>();
if (!string.IsNullOrWhiteSpace(request.CourseName))
expression = expression.And(e => e.CourseName.Contains(request.CourseName));
if (!string.IsNullOrWhiteSpace(request.StudentName))
expression = expression.And(et => et.StudentName.Contains(request.StudentName)); var resultDatas = datas.Where(expression.Compile())
.ToList();
Console.WriteLine($"查询结果:\n{string.Join("\n", resultDatas.Select(e => $"{e.StudentName} {e.CourseName} {e.Score}"))}");

where条件里面只能带委托,而我们的expression是Lambda表达式,所以需要Compile进行委托编译。

运行结果:

仔细看代码,第一个条件And里面的参数是“e”,第二个条件里面的参数是et,同一个Lambda表达式里面(这里只有一个参数),参数肯定是一致的,所以在LambdaExtension类中,在合并两个Lambda表达式的时候,就需要将参数合并成一个。

经过这样的扩展,我们就可以根据我们的实际情况,拼凑好需要的表达式,得到我们想要的结果。

三、总结

表达式树方面的讲解,终于可以告一段落了。一直后没有这样的写文章,现在觉得写文章还是真的挺累的,今年中秋节的这三天,算是全部的给博客园了。不过这三天讲解的内容,基本上把后面Dapper的扩展需要用的技术都铺垫了,后面我们就继续对ORM的讲解了。其实没写一篇博文,蜗牛都会去罗列和梳理相关知识点,这也让蜗牛获益匪浅,也希望蜗牛的博客能帮助到园友,这就是所谓的“赠人玫瑰,手留余香”吧。

C# 表达式树Lambda扩展(四)的更多相关文章

  1. C# 表达式树分页扩展(三)

    一.前言 前面我们知道了表达树的基本知识,也明白了怎么遍历和修改一个表达式,这里我们就一个实际的场景来进行功能开发. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) C# 表达式树 ...

  2. C# 表达式树讲解(一)

    一.前言 一直想写一篇Dpper的定制化扩展的文章,但是里面会设计到对Lambda表达式的解析,而解析Lambda表达式,就必须要知道表达式树的相关知识点.我希望能通过对各个模块的知识点或者运用能够多 ...

  3. C# 表达式树遍历(二)

    一.前言 上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) ...

  4. C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)

    Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...

  5. C#中的Lambda表达式和表达式树

    在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在 ...

  6. Lambda表达式和表达式树

    在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在 ...

  7. Lambda表达式和Lambda表达式树

    LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态. 为了富有效率的使用数据库和其他查询引擎,我们需要一种不同的方式表示管道中的各个操作.即把代码当作可在编程中进行检查的数据. Lambd ...

  8. Lambda表达式树

    1.常量表达式树 Func< + ); 使用表达式树的方式 ConstantExpression a = Expression.Constant(); ConstantExpression b ...

  9. LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树

    序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...

随机推荐

  1. .net软件日常开发规范-基本标准

    一. 基本标准 代码和SQL脚本均不要出现无意义的空格和空行. 所有SQL脚本确保可以重复运行不出错,添加数据的脚本重复运行不会重复添加数据. 能用一行代码或脚本解决的不要写出两行,能用一个方法解决的 ...

  2. Javascript中,实现类与继承的方法和优缺点分析

    Javascript是一种弱类型语言,不存在类的概念,但在js中可以模仿类似于JAVA中的类,实现类与继承 第一种方法:利用Javascript中的原型链 //首先定义一个父类 function An ...

  3. MQTT的学习之Mosquitto安装和使用

    Mosquitto是一个实现了MQTT3.1协议的代理服务器,由MQTT协议创始人之一的Andy Stanford-Clark开发,它为我们提供了非常棒的轻量级数据交换的解决方案.本文的主旨在于记录M ...

  4. spring-boot-plus后台快速开发脚手架之代码生成器使用(十)

    spring-boot-plus 代码生成 Generator 代码生成内容 spring-boot-plus在mybatis-plus基础上,新增param/vo等模板 拓展controller/s ...

  5. Gradle——创建简单的项目

    项目 & 任务 Gradle 的一切都是基于项目和任务的. 构建由一个或多个项目组成.项目的概念很抽象,它取决于你要用Gradle 做什么 .项目可以是 一个 Jar 库或者一个 web 程序 ...

  6. poli-java开源BI软件

    目录 快速入门 Github地址: 特性 一个易于使用的SQL报告应用程序,专为SQL爱好者而设计. SQL中的电源数据分析,可获得更快的业务洞察力. 快速入门 https://shzlw.githu ...

  7. python 23 继承

    目录 继承--inheritance 1. 面向对象继承: 2. 单继承 2.1 类名执行父类的属性.方法 2.2 子类对象执行父类的属性.方法 2.3 执行顺序 2.4 既要执行子类的方法,又要执行 ...

  8. 王某人从0开始学习lorawan的笔记_0

    最近老板想做lorawan的项目,交给我了,我也应承下来了,但是!!!我TM连lorawan是啥子我都不知道啊啊啊啊啊! 真希望我女朋友可以看穿我的倔强,给我1千万,让我专心当舔狗,等等,我的女朋友? ...

  9. Delphi - 使用Pos、Copy函数定位和截取字符串

    使用Pos函数来定位子字符串第一次出现的位置 函数定义: Function Pos(Substr: String, S: String): Integer; 表示取出Substr在S中第一次出现的位置 ...

  10. 第10章 文档对象模型DOM 10.3 Element类型

    Element 类型用于表现 XML或 HTML元素,提供了对元素标签名.子节点及特性的访问. 要访问元素的标签名,可以使用 nodeName 属性,也可以使用 tagName 属性:这两个属性会返回 ...