如何使用 Entity Framework 构造动态查询表达式
一般的程序员做上几年以后, 或多或少的都有些代码的积累, 我也不例外. 作为微软技术程序员, 自从Linq和EF出来之后, 就基本上爱不释手了, 且不说执行效率的问题, 单单就开发效率和代码的可移植性上讲, 都是微软技术常规开发首选无二了. 于是终于丢掉了奴隶社会的.NET三层, 进入了EF的封建制了, 然后, 就发现持久化层的代码越写越少, 越写越精炼, 最后好像简直就是个万金油啊, 拿到哪里哪里可以用. 还有因为Lambda 表达式可以做为查询代码的参数, 就导致, 服务器端单单就数据访问层代码就浓缩到了一个文件中, 而且还不需要写过于复杂的代码就可以写出使用泛型, 适合各种增删改查的动态代码, 如下面的查询:
public IQueryable<T> ExecuteQuery<T>(Fun<T, bool> where, Fun<T, string> orderby, int pageIndex, int pageSize, out int total, params string[] includes) where T : class {...}
一度感觉心里美美的, 好像世界从此就清净了. 直到后来做Web开发的时候, 发现了一个这套机制难以解决的问题, 那就是如何将查询应用到客户端复杂的过滤条件上了?
那为什么不能了? 因为Lambda 表达式本身就是动态委托, 这种类型是不能从客户端(js)构造出来, 作为参数传递到服务器端去执行的, 那么怎么办了?
经过一番研究, 终于发现了可实现的方式, 原来在System.Linq.Expressions命名空间下的Expression类提供了大量方法, 用于构造动态语法, 于是从只需要定义一套查询表达式规则, 然后客户端去写表达式, 服务器端收到后把它解析成Lambda表达式就可以被EF执行了, 至此, 好像发现了新大陆一样又欣喜若狂了一番.
现在, 随着.NET开源, Oracle和Mysql开始支持EF6, 直到Webapi开始支持oData, 貌似一场新的技术改革又要开始了. 我又投入到了新技术孜孜不倦的追求当中.
虽然oData是如此之好用, 以至于从此从客户端接受增删改查什么的都成了浮云, 但是许多程序员还在维护着很多旧代码, 许多新程序员需要技术指导和提高, 许多时间以后我自己或许也会忘记如何去写动态表达式, 所以把核心部分代码贴出来, 大家分享一下, 不喜勿喷哦.
DbContext的扩展查询方法:
1 public static IQueryable<T> ExecuteQuery<T>(this DbContext ctx, QueryExpression query) where T : class
{
try
{
IQueryable<T> queryable = ctx.Set<T>();
if (query != null)
{
if (query.Include != null && query.Include.Count != )
{
foreach (var x in query.Include)
{
queryable = queryable.Include<T>(query.Include[]);
}
}
ParameterExpression param = Expression.Parameter(typeof(T), CommonClass.Anonymous);
if (query.Filter != null && query.Filter.Count != )
{
Expression filter = Expression.Constant(true);
foreach (var x in query.Filter)
{
filter = filter.AddQueryExpression<T>(param, x);
}
Expression<Func<T, bool>> where = arg => true;
where = where.Update(filter, new List<ParameterExpression> { param });
queryable = queryable.Where(where);
}
if (query.OrderBy != null && query.OrderBy.Count != )
{
Expression<Func<T, string>> orderBy = arg => CommonClass.StateField;
IOrderedQueryable<T> orderByQueryable = queryable.OrderBy(orderBy);
foreach (var x in query.OrderBy)
{
Expression exp = Expression.Property(param, x.Field);
orderBy = orderBy.Update(exp, new List<ParameterExpression> { param });
orderByQueryable = x.Asc ? orderByQueryable.ThenBy(orderBy) : orderByQueryable.ThenByDescending(orderBy);
}
queryable = orderByQueryable.AsQueryable<T>();
}
if (query.Pager != null)
{
query.Pager.Total = queryable.Count();
queryable = queryable.Skip((query.Pager.PageIndex - ) * query.Pager.PageSize).Take(query.Pager.PageSize);
}
}
return queryable;
}
catch (Exception ex)
{
throw ex;
}
}
public static Expression AddQueryExpression<T>(this Expression exp, ParameterExpression param, DataFilter filter)
{
try
{
if (param != null && filter != null)
{
Expression mexp = null;
BinaryExpression bexp = null;
PropertyInfo property = typeof(T).GetProperty(filter.Field);
if (property != null)
{
Type type = property.PropertyType;
object value = Convert.ChangeType(filter.Value, type);
switch (filter.Operation)
{
//methods operation
case DataOperation.Contains:
case DataOperation.StartsWith:
case DataOperation.EndsWith:
mexp = Expression.Call(Expression.Property(param, filter.Field), type.GetMethod(filter.Operation.ToString()), Expression.Constant(value));
exp = Expression.AndAlso(exp, mexp);
break;
//other operation
case DataOperation.Equal:
bexp = Expression.Equal(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
case DataOperation.NotEqual:
bexp = Expression.NotEqual(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
case DataOperation.GreaterThan:
bexp = Expression.GreaterThan(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
case DataOperation.GreaterThanOrEqual:
bexp = Expression.GreaterThanOrEqual(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
case DataOperation.LessThan:
bexp = Expression.LessThan(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
case DataOperation.LessThanOrEqual:
bexp = Expression.LessThanOrEqual(Expression.Property(param, filter.Field), Expression.Constant(value));
exp = Expression.AndAlso(exp, bexp);
break;
default:
break;
}
}
}
return exp;
}
catch (Exception ex)
{
throw ex;
}
}
其中的QueryExpression是我自己定义的查询表达式对象, 可以从客户端传递.
打完收工!
如何使用 Entity Framework 构造动态查询表达式的更多相关文章
- 关于Entity Framework自动关联查询与自动关联更新导航属性对应的实体注意事项说明
一.首先了解下Entity Framework 自动关联查询: Entity Framework 自动关联查询,有三种方法:Lazy Loading(延迟加载),Eager Loading(预先加载) ...
- Entity Framework入门教程: Entity Framework支持的查询方式
Entity Framework支持的查询方式有三种 LINQ to Entities Entity SQL Native SQL [LINQ to Entities] LINQ(语言集成查询)是从V ...
- 在Linq to sql 和 Entity framework 中使用lambda表达式实现left join
在Linq to sql 和 Entity framework 中使用lambda表达式实现left join 我们知道lambda表达式在Linq to sql 和 Entity framework ...
- Entity Framework常用的查询方式
Entity Framework支持的查询方式有三种 LINQ to Entities Entity SQL Native SQL [LINQ to Entities] LINQ(语言集成查询)是从V ...
- Entity Framework做IN查询
开发中遇到的Too high level of nesting for select错误 项目使用了Entity Framework结合Mysql, 遇到了一个非常奇怪的性能问题,一个看起来非常简单的 ...
- Entity Framework Core Like 查询揭秘
在Entity Framework Core 2.0中增加一个很酷的功能:EF.Functions.Like(),最终解析为SQL中的Like语句,以便于在 LINQ 查询中直接调用. 不过Entit ...
- Entity Framework中实现查询的几种方法
在介绍几种方法前,献上一张图,希望图的作者不要追究我的盗图之过.本文的内容是我自学时的笔记,自学的内容来自网络.手打的代码,切不可直接复制过去用,会有好多错别字什么的. Entity SQL 类似于S ...
- Entity Framework 7 动态 DbContext 模型缓存 ModelCaching
EF7里实例化DbContext变的有点麻烦了, 下面这个基类会有所帮助: public abstract class BaseDbContext : DbContext { private stri ...
- Entity Framework Linq 动态组合where条件
public static class PredicateExtensions { public static Expression<Func<T, bool>> True&l ...
随机推荐
- asp.net MVC 回顾 Html.ActionLink
在asp.net MVc中想生成一个超链接有很多种方式,通过直接输入<a>.Html.ActionLink.Html.RouteLink等等,今天我们要阐述的就是Html.ActionLi ...
- 自己实现简单的AOP(五)使Demo适应webApi、亦可完成属性自动注入
在前文的Demo中,webApi的Controller是不能自动注入的,原因是 IHttpController 和 IController 是通过两个不同的途径进行激活的. IHttpControll ...
- [修正] 移动平台曲线不平滑的问题(如:TRectangle, TPath...等)
问题:从 XE4 以来,Firemonkey 曲线绘图在移动平台不平滑的问题一直令人诟病,提交到官方的 QC 也是族繁不及备载,官方似乎有意的避开这个问题,迟迟没有修正. 适用版本:XE4 ~ Ber ...
- Hadoop多节点集群安装配置
目录: 1.集群部署介绍 1.1 Hadoop简介 1.2 环境说明 1.3 环境配置 1.4 所需软件 2.SSH无密码验证配置 2.1 SSH基本原理和用法 2.2 配置Master无密码登录所有 ...
- php实现设计模式之 备忘录模式
<?php /*备忘录模式:在不破坏封装的前提下,获取对象的内部状态,并且在对象外保存该状态.这样就可以将该对象恢复到保存之前的状态(行为模式) * * 发起人:记录当前时刻的内部状态,负责定义 ...
- java实现链表
单链表 package com.voole.linkedlist; public class Test { public static void main(String[] args) { Linke ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》-1.通讯框架介绍
[连载]<C#通讯(串口和网络)框架的设计与实现>- 0.前言 目 录 第一章 通讯框架介绍... 2 1.1 通讯的本质... 2 1 ...
- SQL Server 索引
SQL Server 中数据存储的基本单位是页(Page).数据库中的数据文件(.mdf 或 .ndf)分配的磁盘空间可以从逻辑上划分成页(从 0 到 n 连续编号).磁盘 I/O 操作在页级执行.也 ...
- javascript封装与多态的体现
封装是实现面向对象程序设计的第一步,封装就是将数据与函数等集合在一个个的单元中(我们称之为类).被封装的对象通常被称为抽象数据类型. 在传统的面向对象语言中有访问修饰符,如Private:只有类本身能 ...
- iOS之There was an internal API error错误
There was an internal API error. 错误原因:把Product Name作为程序名称,程序名称错乱 解决方法:检查Product Name, 不要包含中文以及特殊字符.在 ...