作者 | 喵叔
责编 | 刘静
出品 | CSDN(ID:CSDNnews)

微软在 Entity Framework Core 2+ 中引入了全局查询过滤器,简化了构建多租户应用程序和实体软删除的复杂度。这篇文章我将通过代码的形式对全局过滤查询进行详细的讲解。在讲解前我们先来简单说一下什么是多租户,所谓多租户简单来说是指一个单独的实例可以为多个组织服务。多租户技术为共用的数据中心内如何以单一系统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。

接下来我们先来看一个例子,我们假定多个租户使用同一个数据库,同一个Schema,区分租户是根据表中的 tId 区分。我们新建一个项目,在项目中重写 DbContext 上下文里的 OnModelCreating 方法,在这个方法中我们使用 HasQueryFilter 方法进行软删除。

public class EFContext : DbContext
{protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Employee>().HasQueryFilter(p => !p.IsDelete);modelBuilder.Entity<Department>().HasQueryFilter(p => !p.IsDelete);base.OnModelCreating(modelBuilder);}
}
上面这段代码大部分人看完一定觉得没有问题,但是我要告诉各位的是这段代码有一个很大的问题,就是每次去操作 Employee 或 Department 实体时都会应用 HasQueryFilter , 那么如果由几十个甚至上百个 Model 的时候,如果按照上述的方式来编写代码的话那么我们将会配置 HasQueryFilter 几十次上百次。那么问题来了,着这种情况下我们应该怎样支持多租户,应该怎样实现软删除,以及应该怎样实现模型查询过滤的自动检测。下面我们就来一个问题一个问题的讲解。
准备基础代码

我们首先准备 Model 代码,代码很简单:

/// <summary>
/// 实体基类
/// </summary>
public class BaseModel
{public int Id { get; set; }//租户Idpublic int TId { get; set; }//是否删除(软删除)public bool IsDelete { get; set; }
}
/// <summary>
/// 员工类
/// </summary>
public class Employee:BaseModel
{public string Name { get; set; }public int Age { get; set; }public Department Department { get; set; }
}
/// <summary>
/// 部门类
/// </summary>
public class Department:BaseModel
{public string DepName { get; set; }public int EmployeeId { get; set; }
}
下面我们实现 Employee 和 Department 的模型映射:
public class EmployeeConfiguration : IEntityTypeConfiguration<Employee>
{public void Configure(EntityTypeBuilder<Employee> builder){builder.ToTable("Employee");builder.HasKey(p => p.Id);builder.Property(p => p.Name);builder.Property(p => p.IsDelete);builder.Property(p => p.Age);builder.HasQueryFilter(p => !p.IsDelete);}
}
public class DepartmentConfiguration : IEntityTypeConfiguration<Department>
{public void Configure(EntityTypeBuilder<Department> builder){builder.ToTable("Department");builder.HasKey(p => p.Id);builder.Property(p => p.DepName);builder.Property(p => p.IsDelete);builder.HasQueryFilter(p => !p.IsDelete);builder.HasMany(p => p.Employees).WithOne(e => e.Department).HasForeignKey(k => k.DepartmentId);}
}
接下来我们再定义一个多租户Id接口和实现,实现里的返回值是定死的,用来模拟检测与当前请求相关的租户。
public interface ITenantProvider
{int GetTId();
}
public class TenantProvider : ITenantProvider
{public int GetTId(){return 1;}
}
最后我们定义查询 Department 的接口和实现:
public interface IDepartmentDb
{IQueryable<Department> GetDepartments();
}
public class DepartmentDb : IDepartmentDb
{private EFContext ef;public DepartmentDb(EFContext _ef){ef = _ef;}public IQueryable<Department> GetDepartments(){var departments = ef.Departments;return departments;}
}
前提条件

如果要为所有实体配置全局查询过滤器,就必须能够自动检测出实体类型,同时类型检测时必须具有缓存支持。基于这两条我们动手创建获取实体类型的接口和实现。首先利用 DependencyContext 获取运行时程序集,将获得的程序集添加到集合中,然后查找出继承自基类 BaseModel 的程序集,如果查找到了就返回,如果没有查找到就实现全局过滤缓存,代码如下:

public interface IEntityTypeProvider
{IEnumerable<Type> GetTypes();
}public class EntityTypeProvider : IEntityTypeProvider
{IList<Type> entityTypeCache;public IEnumerable<Type> GetTypes(){if(entityTypeCache!=null){return entityTypeCache.ToList();}entityTypeCache = (from a in GetReferencingAssemblies()from t in a.DefinedTypeswhere t.BaseType == typeof(BaseModel)select t.AsType()).ToList();return entityTypeCache;}private IEnumerable<Assembly> GetReferencingAssemblies(){var assemblies = new List<Assembly>();var dependencies = DependencyContext.Default.RuntimeLibraries;foreach (var library in dependencies){var assembly = Assembly.Load(new AssemblyName(library.Name));assemblies.Add(assembly);}return assemblies;}}
应用

上一小节我们查找到了继承基类的所有实体,那么现在我们就将全局过滤器应用到实体。

第一步首先,获取租户 id 和前面对应的实现,并注入到上下文构造函数中:

public class EFContext : DbContext
{public DbSet<Employee> Employees;public DbSet<Department> Departments;protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.ApplyConfiguration(new EmployeeConfiguration());modelBuilder.ApplyConfiguration(new DepartmentConfiguration());base.OnModelCreating(modelBuilder);}private int tId;private IEntityTypeProvider entityTypeProvider;public EFContext(DbContextOptions<EFContext> options,ITenantProvider tenantProvider,IEntityTypeProvider entityTypeProvider):base(options){tId = tenantProvider.GetTId();this.entityTypeProvider = entityTypeProvider;}
}
第二步在上下文中定义全局查询过滤的泛型方法:
public void GlobalQuery<T> (ModelBuilder builder) where T :BaseModel
{builder.Entity<T>().HasQueryFilter(e => e.TId == tId && !e.IsDelete);
}
然后在上下文中获取设置全局查询过滤方法的 MethodInfo 类:
static readonly MethodInfo GlobalQueryMethod = typeof(EFContext).GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(p=>p.IsGenericMethod && p.Name== "GlobalQuery");
最后,在OnModelCreating方法中,通过注入的 entityTypeProvider 获取需要全局查询过滤的类型集合,并进行遍历,调用得到进行查询过滤的方法传入 modelBuilder 参数,从而实现多租户查询过滤。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.ApplyConfiguration(new EmployeeConfiguration());modelBuilder.ApplyConfiguration(new DepartmentConfiguration());foreach (var item in entityTypeProvider.GetTypes()){var method = GlobalQueryMethod.MakeGenericMethod(item);method.Invoke(this, new object[] { modelBuilder });}base.OnModelCreating(modelBuilder);
}
总结

这篇文章这是简单的实现了多租户和软删除,队医业务场景更加复杂的项目,我们需要利用一些特殊方法来实现全局查询过滤器。上面所说的方法只是其中一种,不排除由更好的方法。

作者简介:朱钢,笔名喵叔,CSDN博客专家,.NET高级开发工程师,7年一线开发经验,参与过电子政务系统和AI客服系统的开发,以及互联网招聘网站的架构设计,目前就职于北京恒创融慧科技发展有限公司,从事企业级安全监控系统的开发。
声明:本文系作者独立观点,不代表CSDN立场。

【END】

Python的前景怎么样?就业薪资高吗?

https://edu.csdn.net/topic/python115?utm_source=csdn_bw

 热 文 推 荐 

Entity Framework Core 实现全局查询过滤相关推荐

  1. Entity Framework Core 2.0 全局查询过滤器

    本博文翻译自: http://gunnarpeipman.com/2017/08/ef-core-global-query-filters/ Entity Framework Core 2.0 全局查 ...

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

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

  3. Entity Framework Core 5中实现批量更新、删除

    本文介绍了一个在EntityFramework Core 5中不需要预先加载数据而使用一句SQL语句批量更新.删除数据的开发包,并且分析了其实现原理,并且与其他实现方案做了比较. 一.背景 随着微软全 ...

  4. 手把手引进门之 ASP.NET Core Entity Framework Core(官方教程翻译版 版本3.2.5)

    以下是手把手引进门教程,基于 ASP.NET Core, Entity Framework Core ,ABP 框架 创建Web 应用, PS: 自带自动的测试模块哦. 样例下载 (上 github  ...

  5. Entity Framework Core Like 查询揭秘

    在Entity Framework Core 2.0中增加一个很酷的功能:EF.Functions.Like(),最终解析为SQL中的Like语句,以便于在 LINQ 查询中直接调用. 不过Entit ...

  6. Entity Framework Core 执行SQL语句和存储过程

    无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...

  7. Entity Framework Core 软删除与查询过滤器

    注意:我使用的是 Entity Framework Core 2.0 (2.0.0-preview2-final).正式版发布后,功能可能存在变动. 继续探索Entity Framework Core ...

  8. 第一百五十二期:白话Entity Framework Core数据验证

    数据验证是每个项目必须存在的,可以防止不符合系统规范的数据进入系统进而导致系统不稳定甚至崩溃.我们可以自己编写代码进行验证,但是这样一方面代码量较大,另一方面有可能验证代码覆盖不完全.但是在 Enti ...

  9. Entity Framework Core系列教程-25-Entity Framework Core日志

    Entity Framework Core日志 我们经常需要在EF Core中记录SQL并更改跟踪信息以进行调试. EF Core日志记录自动与.NET Core的日志记录机制集成.因此,在隐含使用E ...

最新文章

  1. Oracle 11G在用EXP 导出时,空表不能导出解决
  2. 洛谷 1658 购物
  3. LINQ to XML .Net 3.5 中的新XML对象
  4. python获得当前目录_python如何获取当前工程根目录
  5. cf1562E. Rescue Niwen!
  6. golang plugin模块的使用
  7. oracle 监听拒绝连接,报mybatis映射文件错误Listener refused the connection with the following error: ORA-12519, TNS
  8. python3flask教程_全面的Flask教程[3大部分]
  9. 开放式可编程保险市场Tidal Finance完成由KR1领投的195万美元种子轮融资
  10. N沟道与P沟道增强型MOS管电压、原理、导通条件!
  11. 原神pc端服务器切换工具,基于python3
  12. 雅利安人覆灭了世界三大文明,为何单单在商朝被斩首两万?
  13. 后台Redirect,出现502错误
  14. 逻辑门电路工作原理详解
  15. 秒杀(小米网抢购系统开发实践--“米粉节”背后的故事)
  16. jQuery 特效:盒子破碎和移动动画效果
  17. Kitty中的动态线程池支持Nacos,Apollo多配置中心了
  18. SphereEx 潘娟:玩开源,我们就要秀出别样 My Way 来构建活力生态 | 大话开源Vol.11
  19. vba 抓取 统计用区划和城乡划分代码 到 电子表格
  20. 【附源码】计算机毕业设计SSM社区团购系统

热门文章

  1. LINQ TO OBJECT
  2. [ubuntu] 按文件大小进行排序
  3. python数字识别关键技术_用Python从零开始设计数字图片识别神经网络--搭建基本架构...
  4. python安装rarfile模块_python模块整理7-zipfile模块
  5. swiper 上滑触发_新知 | 为何红酒杯壁挂“眼泪”,骑自行车不会倒,冰面那么滑?...
  6. 记录——《C Primer Plus (第五版)》第八章编程练习第二题
  7. Flutter实战一Flutter聊天应用(十九)
  8. 5 大场景深度探讨何为 Serverless 架构模式?
  9. java中使用tika_Tika基本使用
  10. IE和Windows系统中的彩蛋