Entity Framework Core 2.0 全局查询过滤器
不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址
本博文翻译自:
http://gunnarpeipman.com/2017/08/ef-core-global-query-filters/
Entity Framework Core 2.0 全局查询过滤器
Entity Framework Core 2.0引入了全局查询过滤器,可以在创建模型时应用到实体 。它使得构建多租户应用程序和支持对实体 的软删除变得更加容易。这篇博客文章提供了关于如何在实际应用中使用全局查询过滤器的更深入的概述,以及如何将全局查询过滤器自动应用到领域实体。
示例解决方案。 我在 ASP.NET Core 2中构建了示例解决方案EFCoreGlobalQueryFilters 在更复杂的上下文中演示了全局查询过滤器。它演示了如何自动地将全局查询过滤器应用到领域实体。创建简单的数据库并使用sql脚本填充测试数据。
How global query filters look like?
全局查询过滤器是什么?
这就是全局查询筛选器在软删除时的样子。我们在DbContext类中重写了OnModelCreating方法。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Playlist>().HasKey(e => e.Id);
modelBuilder.Entity<Playlist>().HasQueryFilter(e => !e.IsDeleted);
modelBuilder.Entity<Song>().HasKey(e => e.Id);
modelBuilder.Entity<Song>().HasQueryFilter(e => !e.IsDeleted);
base.OnModelCreating(modelBuilder);
}
这些过滤器,会在我们对给定类型的实体进行查询时应用
真正的应用程序需要什么?
上面的代码是简化的,不考虑实际的应用场景。但是应用程序的体系结构通常是复杂的。所以当我们考虑到作为数字核心或企业一部分任务的关键的应用程序时,创建的将不仅仅是几个类。本文的目标是演示以下内容:
- 如何支持多租户
- 如何支持软删除实体
- 如何自动检测实体
示例解决方案 有助于我们从更复杂的场景开始,但它没有提供完全灵活和复杂的框架。当涉及到现实生活中的应用程序时,涉及的问题太多了,而每个应用程序通常都有自己的解决方案,以解决不同的问题。
定义实体
让我们从定义一些实体开始。他们使用简单的基类,并且期望所有的实体都从基类扩展。
public abstract class BaseEntity
{
public int Id { get; set; }
public Guid TenantId { get; set; }
public bool IsDeleted { get; set; }
}
public class Playlist : BaseEntity
{
public string Title { get; set; }
public IList<Song> Songs { get; set; }
}
public class Song : BaseEntity
{
public string Artist { get; set; }
public string Title { get; set; }
public string Location { get; set; }
}
现在我们有一些简单的实体了,是时候对多租户和软删除的实体进行下一步操作了。
租户提供者
在讨论多租户之前,web应用程序必须有某种方式来检测与当前请求相关的租户。它可以是基于host的header检测,但也可以是别的东西。在这篇文章中我们使用虚拟的提供者以便于我们提供简单的示例。
public interface ITenantProvider
{
Guid GetTenantId();
}
public class DummyTenantProvider : ITenantProvider
{
public Guid GetTenantId()
{
return Guid.Parse("069b57ab-6ec7-479c-b6d4-a61ba3001c86");
}
}
这个提供者必须在启动类的ConfigureServices方法中注册。
创建数据上下文
我希望在这一点上,已经创建了这个数据库,并配置了应用程序来使用它,好了现在让我们从支持租户提供程序的简单数据上下文开始
public class PlaylistContext : DbContext
{
private Guid _tenantId;
private readonly IEntityTypeProvider _entityTypeProvider;
public virtual DbSet<Playlist> Playlists { get; set; }
public virtual DbSet<Song> Songs { get; set; }
public PlaylistContext(DbContextOptions<PlaylistContext> options,
ITenantProvider tenantProvider)
: base(options)
{
_tenantId = tenantProvider.GetTenantId();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Playlist>().HasKey(e => e.Id);
modelBuilder.Entity<Song>().HasKey(e => e.Id);
base.OnModelCreating(modelBuilder);
}
}
现在我们有了可操作的context和租户ID,那么接下来我们就可以对自动创建的全局查询过滤器进行下一步操作了。
检测实体类型
在为所有实体类型添加全局查询过滤器之前,必须检测实体类型。如果我们知道基本实体类型,那么就很容易读取这些类型。但是有一个问题-model是建立在每个请求之上的,而我们每次在创建model时都要扫描程序集,显然这并不是一个好主意。因此,类型检测必须支持某种类型的缓存。下面示例中的这两个方法用于数据上下文类。
private static IList<Type> _entityTypeCache;
private static IList<Type> GetEntityTypes()
{
if(_entityTypeCache != null)
{
return _entityTypeCache.ToList();
}
_entityTypeCache = (from a in GetReferencingAssemblies()
from t in a.DefinedTypes
where t.BaseType == typeof(BaseEntity)
select t.AsType()).ToList();
return _entityTypeCache;
}
private static IEnumerable<Assembly> GetReferencingAssemblies()
{
var assemblies = new List<Assembly>();
var dependencies = DependencyContext.Default.RuntimeLibraries;
foreach (var library in dependencies)
{
try
{
var assembly = Assembly.Load(new AssemblyName(library.Name));
assemblies.Add(assembly);
}
catch (FileNotFoundException)
{ }
}
return assemblies;
}
警告! 如果有单独的服务来返回实体类型,那么在体系结构方面可以更好地理解。在上面的代码中,可以直接使用实体类型变量,而更糟糕的是,可以调用GetReferencingAssemblies方法。如果您编写真正的应用程序,那么最好使用单独的提供程序。
现在,数据上下文知道实体类型,并且可以编写一些代码来获得适用于所有实体的查询过滤器。
将查询过滤器应用于所有实体
这听起来很容易做,但事实并非如此。有些实体类型的列表,并没有直接使用方便的通用方法。在这一点上,需要一个小技巧。我从CodeDump页面找到了解决方案EF-Core 2.0 过滤所有查询 (并试图实现软删除). 这里的代码不能使用,因为这里的数据上下文对ITenantProvider有实例级的依赖关系。但要点仍然是相同的:让我们为数据上下文中的一些通用方法创建通用方法调用。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var type in GetEntityTypes())
{
var method = SetGlobalQueryMethod.MakeGenericMethod(type);
method.Invoke(this, new object[] { modelBuilder });
}
base.OnModelCreating(modelBuilder);
}
static readonly MethodInfo SetGlobalQueryMethod = typeof(PlaylistContext).GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");
public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity
{
builder.Entity<T>().HasKey(e => e.Id);
//Debug.WriteLine("Adding global query for: " + typeof(T));
builder.Entity<T>().HasQueryFilter(e => e.TenantId == _tenantId && !e.IsDeleted);
}
这不是一种简单直观的代码。甚至当我看着这段代码时,我也会瞪大眼睛。即使我看了上百遍,它仍然看起来很疯狂和笨拙。SetGlobalQuery方法也是为实体定义主键的好地方,因为它们都是从相同的基础实体类继承而来的。
测试驱动
如果我们想要了解全局查询过滤器是如何工作的,我们可以使用样例应用程序中的HomeController 来实现这一点。
public class HomeController : Controller
{
private readonly PlaylistContext _context;
public HomeController(PlaylistContext context)
{
_context = context;
}
public IActionResult Index()
{
var playlists = _context.Playlists.OrderBy(p => p.Title);
return View(playlists);
}
}
我修改了默认视图,以显示查询返回的所有播放列表。
@model IEnumerable<Playlist>
<div class="row">
<div class="col-lg-8">
<h2>Playlists</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>Playlist</th>
<th>Tenant ID</th>
<th>Is deleted</th>
</tr>
</thead>
<tbody>
@foreach(var playlist in Model)
{
<tr>
<td>@playlist.Title</td>
<td>@playlist.TenantId</td>
<td>@playlist.IsDeleted</td>
</tr>
}
</tbody>
</table>
</div>
</div>
Web应用程序现在可以运行了。下面是我使用的示例数据。让我们记住,示例应用程序使用的租户ID是069b57ab-6ec7-479c-b6d4-a61ba3001c86。

当运行web应用程序时,将显示下面的表。

当我们比较这两个表时,我们会很容易发现全局查询过滤器在工作中给出的预期结果。
结束
全局查询过滤器是Entity Framework Core 2.0的完美补充,如果没有很多实体,那么我们可以通过文档中给出的简单示例来实现。在更复杂的情况下,需要一些复杂的代码来自动应用全局查询过滤器。希望将来会有更好的解决方案,但目前这里给出的解决方案也做得很好。
欢迎转载,转载请注明翻译原文出处(本文章),原文出处(原博客地址),然后谢谢观看
如果觉得我的翻译对您有帮助,请点击推荐支持:)
Entity Framework Core 2.0 全局查询过滤器的更多相关文章
- .Net Core 2.0生态(4):Entity Framework Core 2.0 特性介绍和使用指南
前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本, ...
- 【EF】Entity Framework Core 2.0 特性介绍和使用指南
阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...
- Entity Framework Core 2.0 中使用LIKE 操作符
Entity Framework Core 2.0 中使用LIKE 操作符 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译 ...
- Entity Framework Core 2.0 使用代码进行自动迁移
一.前言 我们在使用EF进行开发的时候,肯定会遇到将迁移更新到生产数据库这个问题,前面写了一篇文章介绍了Entity Framework Core 2.0的入门使用,这里面介绍了使用命令生成迁移所需的 ...
- ASP.Net Core项目在Mac上使用Entity Framework Core 2.0进行迁移可能会遇到的一个问题.
在ASP.Net Core 2.0的项目里, 我使用Entity Framework Core 2.0 作为ORM. 有人习惯把数据库的连接字符串写在appSettings.json里面, 有的习惯写 ...
- ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0
ASP.NET 5.0 将改名为 ASP.NET Core 1.0 ASP.NET MVC 6 将改名为 ASP.NET MVC Core 1.0 Entity Framework 7.0 将 ...
- [转帖]2016年时的新闻:ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0
ASP.NET Core 1.0.ASP.NET MVC Core 1.0和Entity Framework Core 1.0 http://www.cnblogs.com/webapi/p/5673 ...
- Entity Framework Core 2.0 使用入门
一.前言 Entity Framework(后面简称EF)作为微软家的ORM,自然而然从.NET Framework延续到了.NET Core.以前我也嫌弃EF太重而不去使用它,但是EF Core(E ...
- Entity Framework Core 2.0 入门简介
不多说废话了, 直接切入正题. EF Core支持情况 EF Core的数据库Providers: 此外还即将支持CosmosDB和 Oracle. EFCore 2.0新的东西: 查询: EF.Fu ...
随机推荐
- (转载)IQ 16.0 SP02起支持从压缩文件直接装载数据到表中
参考文档: http://m.blog.chinaunix.net/uid-16765068-id-4405877.htmlhttp://www.cnblogs.com/lichmama/p/4103 ...
- Mybatis(七) mybatis的逆向工程的配置详解
还是觉得看书学习有意思~嘿嘿.今天把mybatis给结束掉. --WH 一.什么是逆向工程? 简单点说,就是通过数据库中的单表,自动生成java代码. Mybatis官方提供了逆向工程,可以针对单表自 ...
- PostGreSQL数据库安装教程
windows 10 x64 pro 1703安装postgresql-9.6.3-2-windows-x64.exe数据库,步骤如下: 第一:下载数据库安装程序,下载地址为:https://www. ...
- PHP运算符知识点
表达式 几乎所写的任何东西都是一个表达式,简单却最精确的定义一个表达式的方式就是"任何有值的东西". 算术运算符 Php中常用的有:+.-.*./.%(取模,得到余数) 左+ - ...
- CJOJ 2307 【一本通】完全背包(动态规划)
CJOJ 2307 [一本通]完全背包(动态规划) Description 设有n种物品,每种物品有一个重量及一个价值.但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干 ...
- SQL Server2008 安装及概述
最近在学习SQL Server 数据库,用的版本是2008 R2的版本,如下图所示,本人学习时间不长,写这篇文章一是为了锻炼自己加深印象,二也可以与和诸多朋友进行交流以及得到大家的指教. 安装教程网上 ...
- 走进json
纯做笔记,请多指教... 1,json格式: 非数组: {"key" : "value"} 数组:[{"key" : "value ...
- 使用Visual Studio加断点调试Unity游戏的C#代码
1.安装Unity5.2.5 2.安装Microsoft Visual Studio 2013 3.打开VS2013,点击Tools | Extensions and Updates,然后在这个界面点 ...
- Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory
阅读源码有助于陶冶情操,本文承接Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory 瞧瞧官方注释 /** * Configur ...
- Ubuntu16.04配置Mac主题
作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7072878.html 觉得有帮助?欢迎来打赏 Ubuntu配置Mac主题 下 ...