传统的操作数据库方式,筛选数据需要用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);
}
}
更改后的测试代码:
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);
}
}
参考:
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;
}
分享到:
相关推荐
EntityFramework数据持久化复习资料4、Lambda表达式的使用(重点内容)(包含源码示例)
Lambda2sql(lambda)->“ sql” 将Java 8 lambda转换为SQL语句。 例如,以下谓词: person -> person.getAge() < 100> 200 转换为字符串: age < 100> 200 允许您以类型安全的方式编写可读的查询。 有关更多...
ef entityframework服务端 封装dll
预备知识 2 LINQ技术 2 LINQ技术的基础 - C#3.0 2 自动属性 2 隐式类型 2 ...为什么要使用Entity Framework,限制条件及当前版本框架的问题 23 EDM中的DML 23 含有Association的EDM的使用 23
EntityFramework.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.dll NuGet 程序包,用以在.NET Core程序中连接SqlServer数据库的组件,当nuget程序包管理器无法正常安装时可以添加此引用到项目中。
Entity Framework 6 Recipes Entity Framework 6 Recipes
Microsoft Entity Framework是 ADO.NET 中的一套支持开发面向数据的软件应用程序的技术,可以使用以下三种技术对EDM进行操作: 1) LINQ query 语言集成查询 (LINQ) 在对象领域和数据领域之间架起了一座桥梁。具有...
Entity Framework Repository(含依赖注入)
Entity Framework Core Cookbook - Second Edition by Ricardo Peres English | 9 Nov. 2016 | ISBN: 1785883305 | 340 Pages | MOBI/EPUB/PDF+Code Files | 6.2 MB Entity Framework is a highly recommended ...
本书是关于Entity framework code first 的详细介绍,在本书中,你可以学到从无到有的创建基于Entity framework code first的项目
电子书 Entity Framework 4 In Action
entityframework框架源代码,需要深入了解entityframework的同学可以下载深入学习研究
Entity Framework Profiler 绿色破解,...需silverlight运行环境,自动监控 Entity Framework ORM产生的SQL语句,是 Entity Framework开发调试的好帮手!!!直接找到SQL短板!!!每年287美元!!!下到你就赚到!!!
Entity Framework 6 (EF6) is a tried and tested object-relational mapper (O/RM) for .NET with many years of feature development and stabilization.
首先介绍一下Entity Framework(个人使用的方式,我没有深入研究),Entity Framework后面简称EF,EF对象关系解决方案,让程序设计者专心于程序设计,不用花时间去研究SQL语句。那么EF是怎么创建数据库,怎么和数据库...
这个资源是使用Entity Framework的时候方便查看Entity Framework生成的SQL语句用的工具,有了这个工具,才能方便的查找性能瓶颈。
Z.EntityFramework.Extensions,基于2019年7月,3.21.2.0去除每月验证和强签名。适合.net4.0及以上
Z.EntityFramework.Extensions 破解 注册机,详细破解方法
Data access is an integral part of any ... You'll learn how to retrieve data by querying the Entity Data Model and understand how to use LINQ to Entities and Entity SQL to query the Entity Data Model.