实体框架高级应用之动态过滤 EntityFramework DynamicFilters
实体框架高级应用之动态过滤 EntityFramework DynamicFilters
我们开门见山,直奔主题。
一、EntityFramework DynamicFilters 是什么,它能做什么?
EntityFramework DynamicFilters是一个开源项目。你可以到这里去下载它的源码。顾名思义,它为我们做的事,就是帮我们动态过滤数据。为了照顾初学者,我们从头道来。
1、何为数据过滤?
数据过滤说简单点,就是去掉我们不想要的数据。SQL语句中的where从句,Linq中的where从句,还有扩展方法Where,就是完成这件光荣任务的。
2、何为动态?
动态的意思就是不死板地应用我们所写的条件,比如,我们在一个地方写了where从句,它只能用于这次查询,下次遇到相似的情况时,我们还得老老实实的写 where xxx=xxx。很长的一段时间,我们一直这样,很和谐地使用着这种方法。突然有一天,抓了抓头:如果类似的情况,能自动加上相应的过虑条件,或是应用相应的规则,该有多好?于是就有动态。当然这里的动态,只是我们面对问题的一个方面。
3、废话半天,它到底能做什么,具体点,好不?
它可以为我们创建全局的,针对实体框架查询的过虑器,这些过滤器会自动应用于每一个查询。能被用于支持多租户,软删除,等等。过滤器能通过返回布尔类型的Linq表达式来创建,同时还支持Contains()操作符(方法)。目前支持的数据库有MS SQL Server(包含 Azure),MySql,Oracle。
二、 没有它时,我们是怎么做的?
我们以软删除(不是真正意义上的删除数据,只是在相应的记录上作一个删除标识)为例。正因为数据没有被真正地删除,只是被我们用一个标识给标记起来了,那么,我们就得在每一个查询的地方加上一个条件(过滤掉标记为删除的数据),代码可能长成这样:
var blogs = context.BlogEntries.Where(b => b.IsDeleted == false).ToList();
上面的代码就不用多解释了,相信你能看明白。 如果是sql 语句,你可能会说,这有什么难的,我找一个地方,把所有的查询拼接上这个条件不就OK。 确实如此,但,这里只是拿这个简单的场景来作为示例,复杂的场景呢?其次,Linq表达式拼接条件 ,不是像字符串那样随心所欲,至少很大一部分人是这样,当然也包含我。每一个查询都手工加上这样的条件,不光是工作量增加了,可维护性降低了,还分散了我们的核心业务逻辑的注意力。
三、EntityFramework DynamicFilters给我们带来了改变
当然,它只是众多解决方案之一,只是作者无私的分享出来了,没把它当成宝供在自己的电脑里。 我只需要在上下文DbContext的OnModelCreating 方法中添加过滤器。代码如下:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); //限制所有针对BlogEntry查询的过虑(只获取未删除的)
//这里的全局过虑,使用了委托,以便在每次需要计算值
//重要:如果值使用的是一个委托,请确保它在你的应用中是全局的,
modelBuilder.Filter("BlogEntryFilter", (BlogEntry b, bool isDeleted) => (b.IsDeleted == isDeleted),
() => false); }
就这样,它就会在我们每一个关于Blog实体的查询中添加上条件(b => b.IsDeleted == false)。我们无需关心它如何添加这个条件,使用的地方,完全透明,就像没有它一样。示例代码如下:
/// <summary>
/// 查询
/// </summary>
/// <param name="context"></param>
/// <param name="userName"></param>
private static void Query(ExampleContext context, string userName)
{
var account = context.Accounts
.Include(a => a.BlogEntries).FirstOrDefault(a => a.UserName == userName);
Console.WriteLine("账号{0}的博客有:",userName);
if (account == null) return;
foreach (var blog in account.BlogEntries)
{
Console.WriteLine("{0}",blog.Id);
}
}
但需要注意的是,如果在同一个上下文DbContext实例中,运用过虑器之前,过虑器有被禁用过,而数据被缓存时,过滤器就不会起任何效果,所有使用时,你一定要避免在同一个上下文中因更改过滤器而影响结果的情况。
如果你在某种情况下不想使用过虑器时,你可以使用如下代码将其禁用:
//禁用过滤器
context.DisableFilter("BlogEntryFilter");
注意:禁用只对当前上下文DbContext实例有效,不影响别的上下文实例。如果你想对所有的上下文实例有效时,可以在 OnModelCreating方法中使用全局禁用函数:
//全局禁用过滤器
modelBuilder.DisableFilterGlobally("BlogEntryFilter");
启用的代码类似,这里就不多少了,直接看代码:
//启用过滤器
context.EnableFilter("BlogEntryFilter");
context.EnableAllFilters();
说了这么多,我们来看看运用过滤器的效果吧,代码如下:
class Program {
static void Main(string[] args) {
// 过滤器默认启用
var context = new ExampleContext();
Console.WriteLine(" 使用过滤器BlogEntryFilter进行查询");
Query(context, "homer");
//禁用过滤器
context.DisableFilter("BlogEntryFilter");
Console.WriteLine(" 禁用过滤器BlogEntryFilter进行查询");
Query(context, "homer");
Console.ReadLine();
}
/// <summary>
/// 查询
/// </summary>
/// <param name="context"></param>
/// <param name="userName"></param>
private static void Query(ExampleContext context, string userName)
{
var account = context.Accounts
.Include(a => a.BlogEntries).FirstOrDefault(a => a.UserName == userName);
Console.WriteLine("账号{0}的博客有:",userName);
if (account == null) return;
foreach (var blog in account.BlogEntries)
{
Console.WriteLine("{0}",blog.Id);
}
}
}
代码输出如下:

四、EntityFramework DynamicFilters原理概述
它是通过在对象 DbModelBuilder 上添加扩展方法Filter实现的,核心代码如下:
private static void Filter<TEntity>(DbModelBuilder modelBuilder, string filterName, LambdaExpression predicate, params object[] valueList)
{
InitializeDynamicFilters(null); filterName = ScrubFilterName(filterName); modelBuilder.Conventions.Add(new DynamicFilterConvention(filterName, typeof(TEntity), predicate)); // Always add the filter to _GlobalParameterValues - need it to be able to disable it
_GlobalParameterValues.TryAdd(filterName, new DynamicFilterParameters()); int numParams = predicate.Parameters == null ? : predicate.Parameters.Count;
int numValues = valueList == null ? : valueList.Length;
for (int i = ; i < numParams; i++)
{
object value = ((i - ) < numValues) ? valueList[i - ] : null;
SetFilterGlobalParameterValue(null, filterName, predicate.Parameters[i].Name, value);
}
}
整个项目的源代码不多,如果你有兴趣,请阅读源代码。文中使用的代码,请于结尾处下载。
最后,我要说明的是,文中并没有把 EntityFramework DynamicFilters的方方面面说完,只是说了一些常见的场景。更多的细节,说阅读源码,或者和大家一起实践、交流。
文中示例源代码下载地址:http://files.cnblogs.com/files/VolcanoCloud/EFDynamicFilterDemo.rar
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
实体框架高级应用之动态过滤 EntityFramework DynamicFilters的更多相关文章
- 读EntityFramework.DynamicFilters源码_心得_整体了解01
前两天同事发给我一个连接地址:实体框架高级应用之动态过滤 EntityFramework DynamicFilters为什么会找到动态过滤的内容,是源于前段时间,我们想做一个个人blog 后端用.NE ...
- C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 ...
- MVC之实体框架(数据持久化框架)EntityFrameWork(EF)
EF - EntityFrameWork 中文名:实体框架(数据持久化框架) 1.使用EF查询(Linq to EF) 1.1使用标准查询运算符来查询 OumindBlogEntities db = ...
- C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(下)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(下)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 ...
- 无法为具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer”。请确保使用限定程序集的名称且该程序集对运行的应用程序可用。有关详细信息,请参阅 http://go.m
Windows服务中程序发布之后会如下错误: 无法为具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“Syste ...
- 关于使用Entity Framework时遇到的问题 未找到具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序的实体框架提供程序。请确保在应用程序配置文件的“entityFramework”节中注册了该提供程序
问题描述: 使用Entity Framework获取数据时报以下错误: 未找到具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序的实体框架提供程序.请确保在应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第四篇:MVC程序中实体框架的连接恢复和 ...
- 用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署
用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署 这是微软官方SignalR 2.0教程Getting Started with En ...
- MVC程序中实体框架的连接恢复和命令拦截
MVC程序中实体框架的连接恢复和命令拦截 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC ...
随机推荐
- javascript面向对象(一):封装
本文来自阮一峰 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握. 下面就是我的学 ...
- Javascript初学篇章_5(对象)
对象 Javascript是一种面向对象的语言,因此可以使用面向对象的思想来进行javascript程序设计对象就是由一些彼此相关的属性和方法集合在一起而构成的一个数据实体.举个例子,一只猫是个对象, ...
- word页码上加横线&&word删除单页页眉
word(2010)页码上加横线 插入——>页脚(选择年刊型)——>如图 然后拖住“竖条条”将页码拖到正中间——>点中页脚右击——>选中“表格属性”——>“边框和底纹”— ...
- AD Local Domain groups, Global groups and Universal groups
http://ss64.com/nt/syntax-groups.html Rules that govern when a group can be added to another group ( ...
- Markdown常用语法
什么是Markdown Markdown 是一种方便记忆.书写的纯文本标记语言,用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档. 通过Markdown简单的语法,就可以使普通文本内容具有 ...
- htmlentities,html_entity_decode,addslashes
PHP htmlspecialchars_decode() 函数 PHP htmlspecialchars() 函数 PHP html_entity_decode() 函数 PHP中混淆的三组函数总结 ...
- 使用 SVN Hook 实现服务器端代码自动更新
之前的做法是客户端提交代码之后,再去服务器端项目中 svn up 一下来更新代码,让服务器端的项目更新到最新版本.可以编写一个 post-commit 钩子脚本来实现服务器端代码的自动更新,它在 SV ...
- PostgreSQL 添加自定义变量
http://dba.stackexchange.com/questions/97095/set-session-custom-variable-to-store-user-id set sessio ...
- QHash
#include <QCoreApplication> #include<QHash> #include<QDebug> int main(int argc, ch ...
- AngularJS基础知识2
一.angularJS双向数据绑定 利用双向数据绑定,不仅能把数据模型的变化同步到视图上面,还可以利用双向数据绑定的特性来做一些样式上面的控制. 双向数据绑定用处很多,不仅仅是像知识点1中的那个例子, ...