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

匿名 (未验证) 提交于 2019-12-02 23:51:01
原文链接:http://www.cnblogs.com/CielWater/p/3399949.html

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

分类: C# Lambda/Linq Entity Framework

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

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

using(var db=new MyDbContext())

{

.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())









叫它假想的原因是执行会产生异常”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;




异常:”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. publicclass
  2. privatereadonly
  3. public
  4. thisnew
  5. publicstatic
  6. returnnew
  7. protectedoverride
  8. ifout
  9. returnbase
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;








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

  1. publicstaticclass
  2. publicstaticboolreturntrue
  3. publicstaticboolreturnfalse
  4. publicstaticthis
  5. new
  6. return
  7. publicstaticboolthisboolbool
  8. return
  9. publicstaticboolthisboolbool
  10. return
    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())





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

  1. usingnew
  2. bool
  3. ifstring
  4. ifnull
  5. ifstring
  6. if
  7. ifstring
  8. ifnull
  9. if
  10. int
  11. new
  12. return

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

转载于:https://www.cnblogs.com/CielWater/p/3399949.html

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

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

using(var db=new MyDbContext())

{

.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())









叫它假想的原因是执行会产生异常”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;




异常:”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. publicclass
  2. privatereadonly
  3. public
  4. thisnew
  5. publicstatic
  6. returnnew
  7. protectedoverride
  8. ifout
  9. returnbase
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;








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

  1. publicstaticclass
  2. publicstaticboolreturntrue
  3. publicstaticboolreturnfalse
  4. publicstaticthis
  5. new
  6. return
  7. publicstaticboolthisboolbool
  8. return
  9. publicstaticboolthisboolbool
  10. return
    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())





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

  1. usingnew
  2. bool
  3. ifstring
  4. ifnull
  5. ifstring
  6. if
  7. ifstring
  8. ifnull
  9. if
  10. int
  11. new
  12. return
文章来源: https://blog.csdn.net/weixin_30377461/article/details/97130109
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!