本文唯一访问地址:http://www.cnblogs.com/yubaolee/p/DynamicLinq.html

对于系统开发来说,按不同字段进行过滤查询是一种常见的需求。在EF中通常的做法是:

        /// <summary>
/// 只是简单举例,只用了两个过滤条件
/// </summary>
IEnumerable<UserInfo> Search(string username = "", string usertype = "")
{
var query = _context.UserInfoes.AsQueryable();
if (!string.IsNullOrEmpty(username))
query = query.Where(u => u.UserName == username);
if (!string.IsNullOrEmpty(usertype))
query = query.Where(u => u.UserType == usertype); return query.ToList();
}

这时如果我有一个新的需求,比如查询用户名中必须包含不定个数关键字的用户。那我们可以用参数数组做类似下面的升级

private IEnumerable<UserInfo> Search(params string[] keys)
{
var query = _context.UserInfoes.AsQueryable(); foreach (var key in keys)
{
query = query.Where(u => u.UserName.Contains(key));
} return query.ToList();
}

上面的代码都是能够良好运行的,这时如果需求变成了:查询用户名中至少包含一个关键字的用户,那我们该如何处理?很明显要用到Or运算,但怎么处理才是最合理的?普通的查询已经不能很好的解决该问题。于是Joe Albahari大神在他的一篇博文中使用PredicateBuilder轻松地解决了该问题:

IQueryable<UserInfo> Search(params string[] keys)
{
var predicate = PredicateBuilder.False<UserInfo>(); foreach (string keyword in keys)
{
predicate = predicate.Or(p => p.UserName.Contains(keyword));
}
return _context.UserInfoes.Where(predicate);
}

至于PredicateBuilder的实现可以去他的博文中查看或者直接在nuget中查找添加LINQKit引用。PredicateBuilder很好的解决的动态生成Lambda问题,支持And/Or等主流运算。但它仍没能解决一个问题:如果查询条件中的属性(即数据库中的字段)也是不确定的,这样该如何处理?

这时Scott大神站出来了。在他的博客Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)中,他把EF整成了拼接SQL的方式来实现这个需求。如下:

  private IQueryable<UserInfo> Search(string key, string value)
{
return _context.UserInfoes.Where("@0 ='@1'", key, value);
}

这样我们就不怕无尽变更的需求,想怎么调用都可以:

 var users = Search("UserNmae", "yubaolee");  //过滤用户名
var users2 = Search("UserType", "administrator"); //过滤用户类型

你也可以使用Key-Value之类的组合成更强大的查询函数。然,世界上的事情总不是那么美好的。你会在实践中用着用着发现,丫竟然不支持 like,用着用着发现,丫竟然不支持guid等等。

唉!我去,我等向来拿来就用之流,竟然会碰到这种鸟事。还是自己动手吧!

好吧,下面才是博文主要内容,如果少年你没看到下面,啧啧!着实有些可惜…

分析一下我们的需求:

  1. 可以根据用户提交过来的字符串还得到他想查找的属性(或数据库字段);
  2. 可以把传过来的操作转成一个比较型的lambda表达式;
  3. 得把该表达式传到EF查询的Where中;

根据上面的需求,我们可以借鉴一下PredicateBuilder的实现方式,用表达式树来生成动态lambda,然后传到ef的过滤条件中。如下:

 public class Filter
{
public string Key { get; set; } //过滤的关键字
public string Value { get; set; } //过滤的值
public string Contract { get; set; }// 过滤的约束 比如:'<' '<=' '>' '>=' 'like'等
} public static class DynamicLinq
{
/// <summary>
/// 创建lambda中的参数,即c=>c.xxx==xx 中的c
/// </summary>
public static ParameterExpression CreateLambdaParam<T>(string name)
{
return Expression.Parameter(typeof(T), name);
} /// <summary>
/// 创建linq表达示的body部分,即c=>c.xxx==xx 中的c.xxx==xx
/// </summary>
public static Expression GenerateBody<T>(this ParameterExpression param, Filter filterObj)
{
PropertyInfo property = typeof(T).GetProperty(filterObj.Key); //组装左边
Expression left = Expression.Property(param, property);
//组装右边
Expression right = null; //todo: 下面根据需要,扩展自己的类型
if (property.PropertyType == typeof(int))
{
right = Expression.Constant(int.Parse(filterObj.Value));
}
else if (property.PropertyType == typeof(DateTime))
{
right = Expression.Constant(DateTime.Parse(filterObj.Value));
}
else if (property.PropertyType == typeof(string))
{
right = Expression.Constant((filterObj.Value));
}
else if (property.PropertyType == typeof(decimal))
{
right = Expression.Constant(decimal.Parse(filterObj.Value));
}
else if (property.PropertyType == typeof(Guid))
{
right = Expression.Constant(Guid.Parse(filterObj.Value));
}
else if (property.PropertyType == typeof(bool))
{
right = Expression.Constant(filterObj.Value.Equals(""));
}
else
{
throw new Exception("暂不能解析该Key的类型");
} //todo: 下面根据需要扩展自己的比较
Expression filter = Expression.Equal(left, right);
switch (filterObj.Contract)
{
case "<=":
filter = Expression.LessThanOrEqual(left, right);
break; case "<":
filter = Expression.LessThan(left, right);
break; case ">":
filter = Expression.GreaterThan(left, right);
break; case ">=":
filter = Expression.GreaterThanOrEqual(left, right);
break; case "like":
filter = Expression.Call(left, typeof(string).GetMethod("Contains", new[] { typeof(string) }),
Expression.Constant(filterObj.Value));
break;
} return filter;
} /// <summary>
/// 创建完整的lambda,即c=>c.xxx==xx
/// </summary>
public static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body)
{
return Expression.Lambda(body, param);
} /// <summary>
/// 创建完整的lambda,为了兼容EF中的where语句
/// </summary>
public static Expression<Func<T, bool>> GenerateTypeLambda<T>(this ParameterExpression param, Expression body)
{
return (Expression<Func<T, bool>>)(param.GenerateLambda(body));
} public static Expression AndAlso(this Expression expression, Expression expressionRight)
{
return Expression.AndAlso(expression, expressionRight);
} public static Expression Or(this Expression expression, Expression expressionRight)
{
return Expression.Or(expression, expressionRight);
} public static Expression And(this Expression expression, Expression expressionRight)
{
return Expression.And(expression, expressionRight);
}
}

来看看我们客户端的调用:

//模拟过滤对象
var filters = new Filter[]
{
new Filter {Key = "UserName", Value = "yubaolee", Contract = "like"},
new Filter {Key = "UserType", Value = "administrator", Contract = "="}
}; var param = DynamicLinq.CreateLambdaParam<UserInfo>("c");
Expression body = Expression.Constant(true); //初始默认一个true
foreach (var filter in filters)
{
body = body.AndAlso(param.GenerateBody<UserInfo>(filter)); //这里可以根据需要自由组合
}
var lambda = param.GenerateTypeLambda<UserInfo>(body); //最终组成lambda var users = _context.UserInfoes.Where(lambda); //得到最终结果
Console.Read();

这时我们可以自由组合,但客户端代码量看起来好像不少。我们来优化封装一下:

public static class DynamicExtention
{
public static IQueryable<T> Where<T>(this IQueryable<T> query, Filter[] filters)
{
var param = DynamicLinq.CreateLambdaParam<T>("c");
Expression body = Expression.Constant(true); //初始默认一个true
foreach (var filter in filters)
{
body = body.AndAlso(param.GenerateBody<T>(filter)); //这里可以根据需要自由组合
}
var lambda = param.GenerateTypeLambda<T>(body); //最终组成lambda
return query.Where(lambda);
}
}

最后看看我们客户端的调用:

 //模拟过滤对象
var filters = new Filter[]
{
new Filter {Key = "UserName", Value = "yubaolee", Contract = "like"},
new Filter {Key = "UserType", Value = "administrator", Contract = "="}
}; var users = _context.UserInfoes.Where(filters); //得到最终结果
Console.Read();

代码如此的干净整洁。而且因为扩展的Where语句是基于泛型的,所以无论你的EF集合是哪种DbSet,都可以直接拿来使用。如果再把过滤类Filter功能深化,扩展成树状结构,那么可以实现种组合查询。哪怕是联表查询也不在话下。

.NET深入实战系列--EF到底怎么写过滤条件的更多相关文章

  1. .NET深入实战系列--EF到底怎么写过滤条件(转)

    原文来自:http://www.cnblogs.com/yubaolee/p/DynamicLinq.html 对于系统开发来说,按不同字段进行过滤查询是一种常见的需求.在EF中通常的做法是: /// ...

  2. [.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst

    一.前言 从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性 ...

  3. [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

    一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...

  4. NET 领域驱动设计实战系列总结

    NET 领域驱动设计实战系列总结 一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核 ...

  5. AspNetCore - MVC实战系列(一)

    本章开篇先简单介绍下最近两周自己利用业余时间做的一个图片收集网站,当然这个是靠用户自己上传来收集不是去抓某些个网站的图片,那样没意义,这里我取名为“爱留图”:该网站的简单介绍大家可以参考下上篇的内容爱 ...

  6. GitHub实战系列~1.环境部署+创建第一个文件 2015-12-9

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  7. Spark入门实战系列--10.分布式内存文件系统Tachyon介绍及安装部署

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Tachyon介绍 1.1 Tachyon简介 随着实时计算的需求日益增多,分布式内存计算 ...

  8. Spark入门实战系列--1.Spark及其生态圈简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .简介 1.1 Spark简介 年6月进入Apache成为孵化项目,8个月后成为Apache ...

  9. Spark入门实战系列--2.Spark编译与部署(上)--基础环境搭建

    [注] 1.该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取: 2.Spark编译与部署将以CentOS 64位操作系统为基础,主要是考虑到实际应用 ...

随机推荐

  1. HTML 获取屏幕、浏览器、页面的高度宽度

    本篇主要介绍Web环境中屏幕.浏览器及页面的高度.宽度信息. 目录 1. 介绍:介绍页面的容器(屏幕.浏览器及页面).物理尺寸与分辨率.展示等内容. 2. 屏幕信息:介绍屏幕尺寸信息:如:屏幕.软件可 ...

  2. Android性能优化之利用Rxlifecycle解决RxJava内存泄漏

    前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学 ...

  3. pt-ioprofile

    pt-ioprofile是用来观察特定进程的IO信息的. 该脚本是用shell写的,有两方面的作用: pt-ioprofile does two things: ) ) is not performe ...

  4. 原生js+css3实现图片自动切换,图片轮播

    运用CSS3transition及opacity属性 制作图片轮播动画 自己这两天根据用js来控制触发CSS3中transition属性,从而写出来的以CSS3动画为基础,js控制过程的图片轮播 运用 ...

  5. C++标准库实现WAV文件读写

    在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件的读写操作,只使用标准C++库函数,不依赖于其他的库. WAV文件结构 ...

  6. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  7. 我的屌丝giser成长记-工作篇之B公司

    从A公司跳槽到B公司,岗位还是webgis开发方向,但是具体实现的技术完全变了,从flex转换js,这也是我要离开A公司的最重要的原意之一:A公司的arcgis for flex框架采用了flexvi ...

  8. BPM助力企业数字化转型

    自九十年代末,流程管理开始引入国内,至今已经有20多年的历史了,由最初的部门级应用向企业级应用转变,大家的认知也经历了一系列的发展变化.不同阶段的信息化水平对企业的流程以及BPM平台也提出了不同的需求 ...

  9. 自定义控件之 圆形 / 圆角 ImageView

    一.问题在哪里? 问题来源于app开发中一个很常见的场景——用户头像要展示成圆的:       二.怎么搞? 机智的我,第一想法就是,切一张中间圆形透明.四周与底色相同.尺寸与头像相同的蒙板图片,盖在 ...

  10. 当我们在谈论kmeans(1)

    本稿为初稿,后续可能还会修改:如果转载,请务必保留源地址,非常感谢! 博客园:http://www.cnblogs.com/data-miner/ 简书:建设中... 知乎:建设中... 当我们在谈论 ...