不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址

本博文翻译自:

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 全局查询过滤器的更多相关文章

  1. .Net Core 2.0生态(4):Entity Framework Core 2.0 特性介绍和使用指南

    前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本, ...

  2. 【EF】Entity Framework Core 2.0 特性介绍和使用指南

    阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...

  3. Entity Framework Core 2.0 中使用LIKE 操作符

    Entity Framework Core 2.0 中使用LIKE 操作符 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译 ...

  4. Entity Framework Core 2.0 使用代码进行自动迁移

    一.前言 我们在使用EF进行开发的时候,肯定会遇到将迁移更新到生产数据库这个问题,前面写了一篇文章介绍了Entity Framework Core 2.0的入门使用,这里面介绍了使用命令生成迁移所需的 ...

  5. ASP.Net Core项目在Mac上使用Entity Framework Core 2.0进行迁移可能会遇到的一个问题.

    在ASP.Net Core 2.0的项目里, 我使用Entity Framework Core 2.0 作为ORM. 有人习惯把数据库的连接字符串写在appSettings.json里面, 有的习惯写 ...

  6. 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    将 ...

  7. [转帖]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 ...

  8. Entity Framework Core 2.0 使用入门

    一.前言 Entity Framework(后面简称EF)作为微软家的ORM,自然而然从.NET Framework延续到了.NET Core.以前我也嫌弃EF太重而不去使用它,但是EF Core(E ...

  9. Entity Framework Core 2.0 入门简介

    不多说废话了, 直接切入正题. EF Core支持情况 EF Core的数据库Providers: 此外还即将支持CosmosDB和 Oracle. EFCore 2.0新的东西: 查询: EF.Fu ...

随机推荐

  1. python多线程爬虫设计及实现示例

    爬虫的基本步骤分为:获取,解析,存储.假设这里获取和存储为io密集型(访问网络和数据存储),解析为cpu密集型.那么在设计多线程爬虫时主要有两种方案:第一种方案是一个线程完成三个步骤,然后运行多个线程 ...

  2. docker~linux下的部署和基本命令

    回到目录 docker是最近比较流行的容器工具,它可以帮助我们快速部署应用,尤其是在“微服务”环境下,成百个服务要去启动,停止,部署一次太麻烦,而如果把它部署到docker里,下一次应用就方便多了,如 ...

  3. 【菜鸟入门】安装配置eclipse 并编写运行第一个Java程序

    不得不吐槽一下,安装配置这eclipse真是太费劲了...下面总结一下,以便下次再安装 本人 win10系统,64位机 一.在官网下载eclipse安装包 文件名:eclipse-inst-win64 ...

  4. JDBC&&c3p0、事务、批处理、多线程 于一体的经典秘方QueryRunner

    目录: 基础篇_功能各自回顾 JDBC基础代码回顾(使用JdbcUtils工具简化) c3p0数据库连接池的使用(使用JdbcUtils工具简化) 大数据的插入(使用c3p0+JdbcUtils工具简 ...

  5. Jmeter之处理session、cookie以及如何做关联

    具体描述问题之前,我们先了解下session.cookie session.cookie的概念 1.session是放在服务器上的,过期与否取决于服务期的设定,cookie是存在客户端的,过去与否可以 ...

  6. (转)目前比较全的CSS重设(reset)方法总结

    在当今网页设计/开发实践中,使用CSS来为语义化的(X)HTML标记添加样式风格是重要的关键.在设计师们的梦想中都存在着这样的一个完美世界:所有的浏览器都能够理解和适用多有CSS规则,并且呈现相同的视 ...

  7. iOS开发实战-时光记账Demo 网络版

    之前写了一个本地数据库版本 戳这里 现在这个就是增加了后台 登录注册页面以及web的上传记录展示页面 含有少量php有兴趣可以看下 另外demo中包括数据库操作.json.网络请求等都没有用到第三方库 ...

  8. HDOJ2008-数值统计

    Problem Description 统计给定的n个数中,负数.零和正数的个数.   Input 输入数据有多组,每组占一行,每行的第一个数是整数n(n<100),表示需要统计的数值的个数,然 ...

  9. EasyUi+Spring Data 实现按条件分页查询

    Spring data 介绍 Spring data 出现目的 为了简化.统一 持久层 各种实现技术 API ,所以 spring data 提供一套标准 API 和 不同持久层整合技术实现 . 自己 ...

  10. hadoop环境中误删除tmp文件夹的恢复

    情景描述: 种种原因,不小心把系统根目录中的tmp文件删除了!发现jps之后看不到 master主机上面的namenode,resourcemanager,secondarynamenode三个进程了 ...