Lambda表达式动态拼接(备忘)

时间:2022-06-23 02:44:01
分类: C# Lambda/Linq Entity Framework 2013-05-24 06:58 491人阅读 评论(0) 收藏 举报

传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句。

在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如:

using(var db=new MyDbContext())

{

var s= db.Students.ToList().First(s=>s.ID=1200);

}

嘣!进行了全表数据读取!当然一般人也不会犯这种低级的错误,言归正传。

可以简单的这样筛选数据:

using(var db=new MyDbContext())

{

var list =db.Students.AsQueryable();

if(********){list=list.Where(s=>s.ID=1200);}

if(******){list=list.Where(...)}

}

但是有时这种方法不能完成特定需求,如:

using(var db=new MyDbContext())

{

var list =db.Students.AsQueryable();

if(条件1){list=list.Where(s=>s.ID>1200);}

if(条件2){list=list.Where(s=>s.ID<1000);}

}

现在条件1和条件2同时成立,得到的是空结果集而不是ID>1200和ID<1000的结果集。

这只是两个并列简单条件的组合,如果是条件嵌套呢?

下面是假想:

using (var db = new MyDbContext())
            {

Expression<Func<Student, bool>> checkStudent1 = s1 => s1.ID > 1200;
                Expression<Func<Student, bool>> checkStudent2 = s2 => s2.ID < 1000;
                var e =
                    Expression.Lambda<Func<Student, bool>>(
                        Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
                var result = db.Students.Where(e).ToList();
            }

叫它假想的原因是执行会产生异常”The parameter 's2' was not bound in the specified LINQ to Entities query expression“。

e的内容是{s1 => ((s1.ID > 1200) Or (s2.ID < 1000))},很明显s2这个参数是没有被定义的。

实际上我们一直操作一个Student表,最终我们想要的也是多Lambda表达式合在一起对该Student表的操作。换句话说,s2应该用s1代替。

有人说了,这样:

Expression<Func<Student, bool>> checkStudent1 = s => s.ID > 1200;
                Expression<Func<Student, bool>> checkStudent2 = s => s.ID < 1000;
                var e =
                    Expression.Lambda<Func<Student, bool>>(
                        Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
                var result = db.Students.Where(e).ToList();

异常:”The parameter 's' was not bound in the specified LINQ to Entities query expression“。

e的内容是{s => ((s.ID > 1200) Or (s.ID < 1000))},现在参数都一样是s了,但其实它们的GUID不同,也就是说它们还是两个不同的参数。

我们需要做的是手工把checkStudent2.Body里面的参数s换成checkStudent1.Body里面的参数s。

ExpressionVisitor可以很好的完成这步操作。拿个别人现成的例子来用:

  1. public class ParameterRebinder : ExpressionVisitor
  2. {
  3. private readonly Dictionary<ParameterExpression, ParameterExpression> map;
  4. public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
  5. {
  6. this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
  7. }
  8. public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
  9. {
  10. return new ParameterRebinder(map).Visit(exp);
  11. }
  12. protected override Expression VisitParameter(ParameterExpression p)
  13. {
  14. ParameterExpression replacement;
  15. if (map.TryGetValue(p, out replacement))
  16. {
  17. p = replacement;
  18. }
  19. return base.VisitParameter(p);
  20. }
  21. }
public class ParameterRebinder : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
} public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
} protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}

更改后的测试代码:

Expression<Func<Student, bool>> checkStudent1 = s => s.ID > 1200;
                Expression<Func<Student, bool>> checkStudent2 = s => s.ID < 1000;
                
                var body2 =
                    ParameterRebinder.ReplaceParameters(
                        checkStudent2.Parameters.Select((s,i)=>new{s,f=checkStudent1.Parameters[i]}).ToDictionary(p=>p.s,p=>p.f), checkStudent2.Body);
                var e =
                    Expression.Lambda<Func<Student, bool>>(
                        Expression.Or(checkStudent1.Body, body2), checkStudent1.Parameters);
                var result = db.Students.Where(e).ToList();

至此表达式顺利拼接完成。当然这样使用还是麻烦,借用别人的扩展类稍微修改一下:

  1. public static class PredicateBuilder
  2. {
  3. public static Expression<Func<T, bool>> True<T>() { return f => true; }
  4. public static Expression<Func<T, bool>> False<T>() { return f => false; }
  5. public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
  6. {
  7. // build parameter map (from parameters of second to parameters of first)
  8. var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
  9. // replace parameters in the second lambda expression with parameters from the first
  10. var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
  11. // apply composition of lambda expression bodies to parameters from the first expression
  12. return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
  13. }
  14. public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
  15. {
  16. return first.Compose(second, Expression.And);
  17. }
  18. public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
  19. {
  20. return first.Compose(second, Expression.Or);
  21. }
  22. }
    public static class PredicateBuilder
{ public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
} public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.And);
} public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.Or);
}
}

参考:http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
完美的动态拼接Lambda表达式如下:

using (var db = new MyDbContext())
            {
                var predicate = PredicateBuilder.True<Student>();
                predicate=predicate.And(s => s.ID > 1200);
                predicate=predicate.Or(s => s.ID < 1000);
                var result = db.Students.Where(predicate).ToList();
            }

下面是一个我自己使用的例子,仅供参考:

  1. using (var db = new SHTrackerDbContext())
  2. {
  3. var predicate = PredicateBuilder.True<Course>();
  4. settings = DecorateSettings(settings);
  5. Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID);
  6. if (!string.IsNullOrEmpty(settings.Quater_Year))
  7. {
  8. checkCourse =
  9. c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
  10. s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));
  11. }
  12. if (settings.QuaterYearArray != null)
  13. {
  14. checkCourse =
  15. c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
  16. s2c =>
  17. s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year))));
  18. }
  19. if (!string.IsNullOrEmpty(settings.DPU_ID))
  20. {
  21. checkCourse =
  22. checkCourse.And(
  23. c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));
  24. }
  25. predicate = predicate.And(checkCourse);
  26. if (settings.IsCheckInstructorName)
  27. {
  28. predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName));
  29. }
  30. if (!string.IsNullOrEmpty(settings.Term))
  31. {
  32. predicate = predicate.And(c => c.TermDescription.Equals(settings.Term));
  33. }
  34. if (settings.TermArray != null)
  35. {
  36. predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription)));
  37. }
  38. if (settings.CourseType != CourseType.All)
  39. {
  40. predicate = predicate.And(c => c.Type == (int) settings.CourseType);
  41. }
  42. var cc =
  43. new CourseCollection(
  44. db.Courses.AsNoTracking()
  45. .Where(predicate)
  46. .OrderByDescending(m => m.ID)
  47. .Skip((pageIndex - 1)*pageSize)
  48. .Take(pageSize)
  49. .ToList(),
  50. db.Courses.AsNoTracking().Where(predicate).Count())
  51. {
  52. PageIndex = pageIndex,
  53. PageSize = pageSize,
  54. Settings = DecorateSettings(settings)
  55. };
  56. return cc;
  57. }

参考网址http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx

Lambda表达式动态拼接(备忘)的更多相关文章

  1. Lambda 表达式动态拼接&period;

    背景: 项目使用EF 查询时需要手动判断条件写.觉得太麻烦就Google 如何动态生成Linq.最后找到了 System.Linq.Dynamic.Core. 这个东西. Scott Guthrie ...

  2. Lambda表达式公共拼接函数(原创)

    #region Lambda公共拼接函数 /// <summary> /// LambdaWhere(枚举) /// </summary> public enum Lambda ...

  3. Lambda表达式动态组装查询条件

    最近比较闲,年底了,项目也进入尾声:每天就是维护一下系统,整理整理文档,整理知识点,这样才觉得有点意思: 问题 在使用Linq的where()查询的时候,不知道大家是怎么动态组装多个查询条件时,是怎么 ...

  4. el表达式动态拼接变量&lowbar;c&colon;set的用法

    转自:https://blog.csdn.net/xb12369/article/details/39581955如 何在${}中使用${},例:${user.name_${user.id}},use ...

  5. 动态LINQ(Lambda表达式)构建

    using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; us ...

  6. Lambda 表达式 是 个 好东东

    Lambda 表达式 是 个 好东东 首先,通过 Lambda 表达式 + 动态语言特性 dynamic , C# 已经 可以 实现 函数式 编程 了 其次, 利用 Lambda, 可以 实现 AOP ...

  7. 表达式树动态拼接lambda

    动态拼接lambda表达式树   前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dappe ...

  8. 动态拼接lambda表达式树

    前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dapperLambda按条件查询时是传入表 ...

  9. 【转】EntityFramework动态组合Lambda表达式作为数据筛选条件,代替拼接SQL语句

    传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句. 在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如: using(var db=ne ...

随机推荐

  1. C语言用分别用递归和循环求数字的阶乘的方法

    以下代码均为 自己 实现,嘻嘻! 参考文章:http://blog.csdn.net/talk_8/article/details/46289683 循环法 int CalFactorial(int ...

  2. html中css、div命名规范

    html中css.div命名规范 1.类class的命名规范示例 头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column ...

  3. redis的主从复制,读写分离,主从切换

    当数据量变得庞大的时候,读写分离还是很有必要的.同时避免一个redis服务宕机,导致应用宕机的情况,我们启用sentinel(哨兵)服务,实现主从切换的功能. redis提供了一个master,多个s ...

  4. Css Study - 纵向Menu - By html and Css

    http://www.wikihow.com/Create-a-Dropdown-Menu-in-HTML-and-CSS HTML <div id="leftmenu"&g ...

  5. Java--&gt&semi;打包发送信息&lpar;UDP协议&rpar;

    --> 好像UDP 协议没有TCP 协议应用得那么广泛 --> UdpSender 类定义一个发送端(快递公司) package com.dragon.java.udpdatagram; ...

  6. SuperSocket&plus;unity 网络笔记

    学习SuperSocket 必须要注意的 代码是 static void Main(string[] args) { WebSocketServer appServer = new WebSocket ...

  7. 从零开始学习PYTHON3讲义(八)列表类型跟冒泡排序

    <从零开始PYTHON3>第八讲 ​前面我们见过了不少的小程序,也见过了不少不同类型的变量使用的方法.但目前我们涉及到的,还都是单个的变量和单个的立即数.以变量来说,目前我们见到的,基本都 ...

  8. 基于TFTP方式加载启动Linux内核

            一.软硬件平台 1.开发板:创龙AM3359核心板,网口采用RMII形式. 2.UBOOT版本:U-Boot-2016.05,采用FDT和DM. 3.交换芯片MARVELL的88E63 ...

  9. Data - References

    01 - 数据分析与数据挖掘的知识列表 图解 知识列表 |关注方面|初级数据分析师|高级数据分析师|数据挖掘工程师| |--------|--------|--------|--------| | 数 ...

  10. 监控SQLServer 数据库表每天的空间变化情况

    阅读完桦仔的<分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)>后,我想使用文中提供的代码做一个统计表每天的新增行数及新增存储空间的功能 实现步骤如下: 1 ...