一、前言

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

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

表达式系列目录

C# 表达式树讲解(一)

C# 表达式树遍历(二)

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

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

二、Lambda扩展

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

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

  1. public class ScoreRequest
  2. {
  3. public string CourseName { get; set; }
  4. public string StudentName { get; set; }
  5. }

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

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

数据源类

  1. public class ScoreClass
  2. {
  3. public string CourseName { get; set; }
  4. public string StudentName { get; set; }
  5. public decimal Score { get; set; }
  6. }

添加数据

  1. var datas = new List<ScoreClass>();
  2. datas.Add(new ScoreClass
  3. {
  4. CourseName = "数学",
  5. StudentName = "学生A",
  6. Score = 60
  7. });
  8. datas.Add(new ScoreClass
  9. {
  10. CourseName = "数学",
  11. StudentName = "学生B",
  12. Score = 65
  13. });
  14. datas.Add(new ScoreClass
  15. {
  16. CourseName = "数学",
  17. StudentName = "学生C",
  18. Score = 70
  19. });
  20. datas.Add(new ScoreClass
  21. {
  22. CourseName = "数学",
  23. StudentName = "学生D",
  24. Score = 75
  25. });
  26. datas.Add(new ScoreClass
  27. {
  28. CourseName = "数学",
  29. StudentName = "学生E",
  30. Score = 80
  31. });
  32. datas.Add(new ScoreClass
  33. {
  34. CourseName = "数学",
  35. StudentName = "学生F",
  36. Score = 81
  37. });
  38. datas.Add(new ScoreClass
  39. {
  40. CourseName = "数学",
  41. StudentName = "学生G",
  42. Score = 82
  43. });
  44. datas.Add(new ScoreClass
  45. {
  46. CourseName = "数学",
  47. StudentName = "学生H",
  48. Score = 83
  49. });
  50. datas.Add(new ScoreClass
  51. {
  52. CourseName = "数学",
  53. StudentName = "学生I",
  54. Score = 84
  55. });

好了现在我们就查询数据

  1. var request = new ScoreRequest()
  2. {
  3. CourseName = "数",
  4. StudentName = "H"
  5. };
  6. var resultDatas = datas.Where(e => e.CourseName.Contains(request.CourseName) && e.StudentName.Contains(request.StudentName))
  7. .ToList();

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

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

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

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

  1. public static class LambdaExtension
  2. {
  3. public static Expression<Func<T, bool>> True<T>() { return param => true; }
  4. public static Expression<Func<T, bool>> False<T>() { return param => false; }
  5. public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
  6. {
  7. return first.Compose(second, Expression.AndAlso);
  8. }
  9. public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
  10. {
  11. return first.Compose(second, Expression.OrElse);
  12. }
  13. private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
  14. {
  15. var map = first.Parameters
  16. .Select((f, i) => new { f, s = second.Parameters[i] })
  17. .ToDictionary(p => p.s, p => p.f);
  18. var secondBody = PFTParameterExtension.ReplaceParameters(map, second.Body);
  19. return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
  20. }
  21.  
  22. private class PFTParameterExtension : ExpressionVisitor
  23. {
  24. private readonly Dictionary<ParameterExpression, ParameterExpression> map;
  25.  
  26. public PFTParameterExtension()
  27. {
  28.  
  29. }
  30.  
  31. public PFTParameterExtension(Dictionary<ParameterExpression, ParameterExpression> map)
  32. {
  33. this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
  34. }
  35.  
  36. /// <summary>
  37. /// 替换参数
  38. /// </summary>
  39. /// <param name="map">The map.</param>
  40. /// <param name="exp">The exp.</param>
  41. /// <returns>Expression</returns>
  42. public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
  43. {
  44. return new PFTParameterExtension(map).Visit(exp);
  45. }
  46.  
  47. protected override Expression VisitParameter(ParameterExpression p)
  48. {
  49. ParameterExpression replacement;
  50. if (map != null && map.Count > 0 && map.TryGetValue(p, out replacement))
  51. {
  52. p = replacement;
  53. }
  54. return base.VisitParameter(p);
  55. }
  56.  
  57. }
  58.  
  59. }

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

下面是调用方式

  1. var expression = LambdaExtension.True<ScoreClass>();
  2. if (!string.IsNullOrWhiteSpace(request.CourseName))
  3. expression = expression.And(e => e.CourseName.Contains(request.CourseName));
  4. if (!string.IsNullOrWhiteSpace(request.StudentName))
  5. expression = expression.And(et => et.StudentName.Contains(request.StudentName));
  6.  
  7. var resultDatas = datas.Where(expression.Compile())
  8. .ToList();
  9. 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. JVM总结(二)

    JVM总结(2)java内存区域.字节码执行引擎 1.内存区域 程序计数器:知道线程执行位置,保证线程切换后能恢复到正确的执行位置. 虚拟机栈:存栈帧.栈帧里存局部变量表.操作栈.动态连接.方法返回地 ...

  2. GIS历史概述与WebGis应用开发技术浅解

    声明:本篇在李晓晖的<杂谈WebGIS>,补充更多的资料说明.基于地图二次开发一直断断续续在做,这里算是补充一下基本功把.其实对于前端,WebGis开发都是api,抄demo,改.GIS深 ...

  3. 弹性盒子---CSS3布局方式

    1.弹性盒子/伸缩盒子 如果要使用弹性盒子属性,首先要将父级元素变成弹性盒子 Flex-direction 设置伸缩盒子的内部元素的排列方式 Row    从左到右安行排列 Column  从上到下按 ...

  4. 以股票案例入门基于SVM的机器学习

    SVM是Support Vector Machine的缩写,中文叫支持向量机,通过它可以对样本数据进行分类.以股票为例,SVM能根据若干特征样本数据,把待预测的目标结果划分成“涨”和”跌”两种,从而实 ...

  5. 某团面试题:JVM 堆内存溢出后,其他线程是否可继续工作?

    转载注明:http://dwz.win/gHc 最近网上出现一个美团面试题:"一个线程OOM后,其他线程还能运行吗?".我看网上出现了很多不靠谱的答案.这道题其实很有难度,涉及的知 ...

  6. case when多条件

    SELECT label ,label3 ,lon_cen ,lat_cen ,lon3 ,lat3 ,antenna_height ,horizontal_angle ,CASE WHEN roun ...

  7. 如何在不到12天的时间里将网站权重优化到1(纯白帽SEO方法)

    之前操作了一个IDC网站,不到1个月的时间把网站的权重从0做到了1,本来想写篇文章分享相关的操作经验.后来因为网站整体规划的原因,IDC网站需要关闭一段时间做备案的更新,排名肯定就会掉了,然后怕大家看 ...

  8. JavaScript在web自动化测试中的作用

    前言 JS的全称JavaScript,是一种运行在浏览器中的解释型脚本语言,通常用来实现web前端页面的基本功能,对于前端开发人员是不得不掌握的一门基本技能,但是对于做web自动化测试的人员来说,如果 ...

  9. freemarker导出复杂样式的Excel

    freemarker导出复杂样式的Excel 代码地址: gitee https://gitee.com/suveng/demo/tree/master/chapter.002 代码存放于demo下面 ...

  10. Java 集合源码分析(一)HashMap

    目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...