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

本博文翻译自:

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. mybatis学习笔记(四)-- 为实体类定义别名两种方法(基于xml映射)

    下面示例在mybatis学习笔记(二)-- 使用mybatisUtil工具类体验基于xml和注解实现 Demo的基础上进行优化 以新增一个用户为例子,原UserMapper.xml配置如下: < ...

  2. AddNewsServlet -servlet处理响应请求

    package com.pb.news.web.servlet; import java.io.File;import java.io.IOException;import java.util.Dat ...

  3. 移动端300ms的点击延迟以及解决方案

    [今天做在移动端的一些效果时,我选择使用动画而不是用过渡,这个300ms的点击延迟是我为什么使用动画而不使用过渡最主要的一个原因] 动画和过渡 共同点:都是css控制DOM运动, 不同点: 1.过渡: ...

  4. Unity3D-Shader-实现X光效果

    [旧博客转移 - 2016年1月3日 16:40 ] 最近学习了一些Shader效果,打算把学到的知识总结一下,这篇讲一下这种轮廓发光的效果(如下图所示),也有一些地方管这个叫X光     1.原理 ...

  5. MySql单表最大8000W+ 之数据库遇瓶颈记

    前言 昨晚救火到两三点,早上七点多醒来,朦胧中醒来发现电脑还开着,赶紧爬起来看昨晚执行的SQL命令结果.由于昨晚升级了阿里云的RDS,等了将近两个小时 还在 升降级中,早上阿里云那边回复升级过程中出现 ...

  6. MetaWebBlogApi开发经验

    背景 花了一天的时间为某笔记软件开发了一款插件,能够发布笔记到博客园,本文就是记录开发时遇到的问题和解决方案,希望有大佬能开发出更好用的博客编辑器. 为什么要开发? 现在有很多开源的建站工具hexo, ...

  7. 纯 CSS 实现波浪效果!

    一直以来,使用纯 CSS 实现波浪效果都是十分困难的. 因为实现波浪的曲线需要借助贝塞尔曲线. 而使用纯 CSS 的方式,实现贝塞尔曲线,额,暂时是没有很好的方法. 当然,借助其他力量(SVG.CAN ...

  8. Java 基本语法----关键字、标识符

    关键字 关键字的定义和特点 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)特点:关键字中所有字母都为小写 用于定义数据类型的关键字 class interface enum byte ...

  9. windows下安装DB2数据库以及使用Aqua Data Studio链接数据库

    本文只是作为自己的心得体会,不具有一般性! 1.其实安装DB2数据库还是比较简单的,一般都是直接下一步下一步就可以了,只是有些地方需要注意.我安装的DB2数据库版本如下图所示: 2.拿到数据库的版本之 ...

  10. Win7 JBOSS的下载安装、环境变量配置以及部署

    1. 下载安装 http://jbossas.jboss.org/downloads/ 我下载的是:JBoss AS7.1.1.Final 2. 解压安装包  D:\Java\jboss-as-7.1 ...