Lambda表达式动态拼接(备忘)
传统的操作数据库方式,筛选数据需要用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可以很好的完成这步操作。拿个别人现成的例子来用:
- 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);
 - }
 - }
 
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();
至此表达式顺利拼接完成。当然这样使用还是麻烦,借用别人的扩展类稍微修改一下:
- 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);
 - }
 - }
 
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();
            }
下面是一个我自己使用的例子,仅供参考:
- using (var db = new SHTrackerDbContext())
 - {
 - var predicate = PredicateBuilder.True<Course>();
 - settings = DecorateSettings(settings);
 - Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID);
 - if (!string.IsNullOrEmpty(settings.Quater_Year))
 - {
 - checkCourse =
 - c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
 - s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));
 - }
 - if (settings.QuaterYearArray != null)
 - {
 - checkCourse =
 - c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
 - s2c =>
 - s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year))));
 - }
 - if (!string.IsNullOrEmpty(settings.DPU_ID))
 - {
 - checkCourse =
 - checkCourse.And(
 - c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));
 - }
 - predicate = predicate.And(checkCourse);
 - if (settings.IsCheckInstructorName)
 - {
 - predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName));
 - }
 - if (!string.IsNullOrEmpty(settings.Term))
 - {
 - predicate = predicate.And(c => c.TermDescription.Equals(settings.Term));
 - }
 - if (settings.TermArray != null)
 - {
 - predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription)));
 - }
 - if (settings.CourseType != CourseType.All)
 - {
 - predicate = predicate.And(c => c.Type == (int) settings.CourseType);
 - }
 - var cc =
 - new CourseCollection(
 - db.Courses.AsNoTracking()
 - .Where(predicate)
 - .OrderByDescending(m => m.ID)
 - .Skip((pageIndex - 1)*pageSize)
 - .Take(pageSize)
 - .ToList(),
 - db.Courses.AsNoTracking().Where(predicate).Count())
 - {
 - PageIndex = pageIndex,
 - PageSize = pageSize,
 - Settings = DecorateSettings(settings)
 - };
 - return cc;
 - }
 
参考网址http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
Lambda表达式动态拼接(备忘)的更多相关文章
- Lambda 表达式动态拼接.
		
背景: 项目使用EF 查询时需要手动判断条件写.觉得太麻烦就Google 如何动态生成Linq.最后找到了 System.Linq.Dynamic.Core. 这个东西. Scott Guthrie ...
 - Lambda表达式公共拼接函数(原创)
		
#region Lambda公共拼接函数 /// <summary> /// LambdaWhere(枚举) /// </summary> public enum Lambda ...
 - Lambda表达式动态组装查询条件
		
最近比较闲,年底了,项目也进入尾声:每天就是维护一下系统,整理整理文档,整理知识点,这样才觉得有点意思: 问题 在使用Linq的where()查询的时候,不知道大家是怎么动态组装多个查询条件时,是怎么 ...
 - el表达式动态拼接变量_c:set的用法
		
转自:https://blog.csdn.net/xb12369/article/details/39581955如 何在${}中使用${},例:${user.name_${user.id}},use ...
 - 动态LINQ(Lambda表达式)构建
		
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; us ...
 - Lambda 表达式 是 个 好东东
		
Lambda 表达式 是 个 好东东 首先,通过 Lambda 表达式 + 动态语言特性 dynamic , C# 已经 可以 实现 函数式 编程 了 其次, 利用 Lambda, 可以 实现 AOP ...
 - 表达式树动态拼接lambda
		
动态拼接lambda表达式树 前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dappe ...
 - 动态拼接lambda表达式树
		
前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dapperLambda按条件查询时是传入表 ...
 - 【转】EntityFramework动态组合Lambda表达式作为数据筛选条件,代替拼接SQL语句
		
传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句. 在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如: using(var db=ne ...
 
随机推荐
- Liferay 6.2 改造系列之十二:修改Portal设置页面表单内容
			
将Portal设置页面中无用的内容删除: 在/portal-master/portal-impl/src/portal.properties文件中,有如下配置: # # Input a list of ...
 - 在CSDN中添加友情连接
			
<a bref='http://www......'>友情连接</a><br/> <a bref='http://www......'>友情连接2< ...
 - AOP动态代理解析4-jdk代理的实现
			
JDKProxy的使用关键是创建自定义的InvocationHandler,而InvocationHandler中包含了需要覆盖的函数getProxy,而当前的方法正是完成了这个操作.在此确认一下JD ...
 - Collection与Map
			
20145217 <Java程序设计>第5周学习总结(2) 教材学习内容总结 程序中常有收集对象的需求 9.1collection架构 收集对象的行为,像是新增对象的add()方法.移除对 ...
 - BestCoder Round #68 (div.2)
			
并查集 1002 tree 题意:中文题面 分析:(官方题解)把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ansi=siz ...
 - Ue4全景图制作设想
			
官方有个Scene Capture Cube与Cube Rander Target. 之后再想办法生成文件就好了吧
 - zookeeper定时清理log
			
在zookeeper的目录下新建一个脚本,内容如下(zookeeper bin下面也有zkCleanup.sh脚本,原理一样,都是调用java类) shell_dir=$(cd ")&quo ...
 - html表单元素的colspan和rowspan
			
colspan和rowspan这两个属性用于创建特殊的表格. colspan用来指定单元格横向跨越的列数:colspan就是合并列的,colspan=2的话就是合并两列. rowspan用来指定单元格 ...
 - OpenCV 第一课(安装与配置)
			
OpenCV 第一课(安装与配置) win10,opencv-2.4.13, 安装, vs2013, 配置 下载安装软件 官网OpenCV下载地址下载最新版本,我下载的是opencv.2.4.13,然 ...
 - [R]R下as.Date()函数的坑
			
问题描述: 在本地使用as.Date()函数从POSIXct类型中提取date时,出现了日期不一致的错误.导致处理数据时,总是出问题. 还好被领导发现数据有误,不然这个bug不知道还要潜伏多久.尽管如 ...