ABP中的数据过滤器

  本文首先介绍了ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),然后介绍了如何实现一个自定义过滤器,最后介绍了在软件开发过程中遇到的实际问题,同时给出了解决问题的一个未必最优的思路。

一.预定义过滤器

  ABP中的数据过滤器源码在Volo.Abp.Data[2]包中,官方定义了2个开箱即用的过滤器,分别是软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),想必大家对这2个内置的过滤器已经比较熟悉了。下面重点说下通过IDataFilter实现局部过滤,和通过AbpDataFilterOptions实现全局过滤。

1.IDataFilter局部过滤

  主要的思路就是通过IDataFilter依赖注入,然后通过_dataFilter.Disable<XXX>()临时的启用或者禁用过滤器:

namespace Acme.BookStore
{public class MyBookService : ITransientDependency{private readonly IDataFilter _dataFilter;private readonly IRepository<Book, Guid> _bookRepository;public MyBookService(IDataFilter dataFilter, IRepository<Book, Guid> bookRepository){_dataFilter = dataFilter;_bookRepository = bookRepository;}public async Task<List<Book>> GetAllBooksIncludingDeletedAsync(){// 临时禁用ISoftDelete过滤器using (_dataFilter.Disable<ISoftDelete>()){return await _bookRepository.GetListAsync();}}}
}

这样就会局部地把IsDeleted=1的记录查找出来。

2.AbpDataFilterOptions全局过滤

主要是通过选项(Options)的方式来配置全局过滤:

Configure<AbpDataFilterOptions>(options =>
{options.DefaultStates[typeof(ISoftDelete)] = new DataFilterState(isEnabled: false);
});

这样就会全局地把IsDeleted=1的记录查找出来。其中的一个问题是,这段代码写到哪里呢?自己是写到XXX.Host->XXXHostModule->ConfigureServices中,比如Business.Host->BusinessHostModule->ConfigureServices。

二.自定义过滤器

  自定义过滤器是比较简单的,基本上都是八股文格式了,对于EFCore来说,就是重写DbContext中的ShouldFilterEntity和CreateFilterExpression方法。因为暂时用不到MongoDB,所以不做介绍,有兴趣可以参考[1],也不是很难。下面通过一个例子来介绍下EF Core的自定义过滤器。

1.定义过滤器接口

首先定义一个过滤器接口,然后实现该接口:

public interface IIsActive
{bool IsActive { get; }
}public class Book : AggregateRoot<Guid>, IIsActive
{public string Name { get; set; }public bool IsActive { get; set; } //Defined by IIsActive
}

2.重写DbContext中的方法

然后就是重写DbContext中的ShouldFilterEntity和CreateFilterExpression方法:

protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled<IIsActive>() ?? false;protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
{if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))){return true;}return base.ShouldFilterEntity<TEntity>(entityType);
}protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{var expression = base.CreateFilterExpression<TEntity>();if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))){Expression<Func<TEntity, bool>> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property<bool>(e, "IsActive");expression = expression == null ? isActiveFilter : CombineExpressions(expression, isActiveFilter);}return expression;
}

  突然看上去觉得这个自定义过滤器好复杂,后来想想那ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant)是如何实现的呢?然后就找到了源码ABP/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs4398c7d317a45bf1acd88fca9be5c1a0.png看了源码实现后会发现格式一模一样,所以自定义过滤器使用起来没有这么复杂。

三.遇到的实际问题

  假如在SaaS系统中,有一个主中心和分中心的概念,什么意思呢?就是在主中心中可以看到所有分中心的User数据,同时主中心可以把一些通用的资料(比如,科普文章)共享给分中心。在ABP群里问了下,有人建议宿主就是宿主,用来做租户管理的,不能把它当成一个租户,这是一个父子租户的问题。有人建议搞一个仿租户ID过滤器,这样既能曲线解决问题,又不背离宿主和租户的原则。父子租户第一次听说,所以暂不考虑。因为系统已经开发了一部分,如果每个实体都继承仿租户ID过滤器接口,那么也觉得麻烦。
  最终选择把主中心当成是宿主用户,分中心当成是租户。对于一些通用的资料(比如,科普文章),在增删改查中直接IDataFilter局部过滤。比如查找实现如下:

public async Task<PagedResultDto<ArticleDto>> GetAll(GetArticleInputDto input)
{// 临时禁用掉IMultiTenant过滤器using (_dataFilter.Disable<IMultiTenant>()){var query = (await _repository.GetQueryableAsync()).WhereIf(!string.IsNullOrWhiteSpace(input.Filter), a => a.Title.Contains(input.Filter));var totalCount = await query.CountAsync();var items = await query.OrderBy(input.Sorting ?? "Id").Skip(input.SkipCount).Take(input.MaxResultCount).ToListAsync();var dto = ObjectMapper.Map<List<Article>, List<ArticleDto>>(items);return new PagedResultDto<ArticleDto>(totalCount, dto);}
}

  对于"主中心中可以看到所有分中心的User数据"这个问题,因为只是涉及到查看,不做增删改,所以又新建了一个User查找接口,在该接口中直接IDataFilter局部过滤。这样新建的User查找接口就可以看到所有分中心的数据,原来的User查找接口仅能看到宿主或者租户的User数据。总之,适合自己需求的架构就是最好的,如果架构满足不了需求了,那么就迭代架构。

参考文献:
[1]数据过滤:https://docs.abp.io/zh-Hans/abp/6.0/Data-Filtering
[2]Volo.Abp.Data:https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.Data
[3]EntityFramework.DynamicFilters:https://github.com/zzzprojects/EntityFramework.DynamicFilters
[4]ABP文档笔记 - 数据过滤:https://www.cnblogs.com/wj033/p/6494879.html
[5]ABP领域层 - 数据过滤器:https://www.kancloud.cn/gaotang/abp/225839
[6]Mastering-ABP-Framework:https://github.com/PacktPublishing/Mastering-ABP-Framework
[7]ABP多租户:https://docs.abp.io/zh-Hans/abp/6.0/Multi-Tenancy
[8]ASP.NET Boilerplate中文文档:https://www.kancloud.cn/gaotang/abp/225819
[9]详解ABP框架中数据过滤器与数据传输对象使用:https://wenku.baidu.com/view/ec237e90b3717fd5360cba1aa8114431b80d8e5e
[10]ASP.NET Boilerplate官方文档:https://aspnetboilerplate.com/Pages/Documents/Introduction
[11]How to create a custom data filter with EF Core:https://support.aspnetzero.com/QA/Questions/4752/How-to-create-a-custom-data-filter-with-EF-Core

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ActiveMQ与spring整合

2019独角兽企业重金招聘Python工程师标准>>> 1 生产者 第一步&#xff1a;引用相关的jar包。 <dependency> <groupId>org.springframework</groupId><artifactId>spring-jms</artifactId> </dependency> <dependency><…

最新远程部署运维工具汇总

一&#xff0e;Puppet 转载https://baike.baidu.com/item/puppet/5109503?fraladdin puppet是一种Linux、Unix、windows平台的集中配置管理系统&#xff0c;使用自有的puppet描述语言&#xff0c;可管理配置文件、用户、cron任务、软件包、系统服务等。puppet把这些系统实体…

Kali Linux 2016.2初体验使用总结

Kali Linux 2016.2初体验使用总结Kali Linux官方于8月30日发布Kali Linux 2016的第二个版本Kali Linux 2016.2。该版本距离Kali Linux 2016.1版本发布&#xff0c;已经有7个月。在这期间&#xff0c;在Kali Linux 2016.2版本发布的这段时间&#xff0c;Kali Linux官方增补了94个…

Kafka入门教程:学习总结目录索引

【Kafka】| 总结/Edison ZhouEdison总结了Kafka的学习征途系列&#xff0c;特意整理了一份目录索引&#xff0c;希望对你有帮助。0Kafka学习路径在学习Kafka的途中&#xff0c;我总结了一个系列的Kafka学习征途系列教程&#xff0c;它只选取了我认为最实用的部分整理出来&#…

javaweb学习中的路径问题

1. 项目结构 2. 客户端路径 1. 超链接 <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/…

步步为营-11-ListT泛型的简单练习

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 集合简单练习 {class Program{static void Main(string[] args){}private static void Test3(){//奇偶分拣,奇数在前偶数在后List<int>…

SPI 与 API的区别

背景 Java 中区分 API 和 SPI&#xff0c;通俗的讲&#xff1a;API 和 SPI 都是相对的概念&#xff0c;他们的差别只在语义上&#xff0c;API 直接被应用开发人员使用&#xff0c;SPI 被框架扩展人员使用 理解 API &#xff08;Application Programming Interface&#xff09; …

JS---------正则表达式

2019独角兽企业重金招聘Python工程师标准>>> 1.正则表达式的创建方式&#xff1a; 1.1 var reg new RegExp(pattern); 1.2 var reg /^正则规则$/ 2.正则规则&#xff1a; 2.1 [0-9] 代表数字 [A-Z] 代表大写字母 [a-z] 代表小写字母 [A-z] 代表字母 \w 查找单词字…

关于缓存异常:缓存雪崩、击穿、穿透的解决方案

关于缓存雪崩、击穿、穿透的解决方案 前言缓存雪崩 缓存雪崩的原因解决方案缓存击穿 解决方案缓存穿透 解决方案 布隆过滤器 布隆过滤器原理布隆过滤器如何使用在Java中使用布隆过滤器前言 关于缓存异常&#xff0c;我们常见的有三个问题&#xff1a;缓存雪崩、缓存击穿、缓存穿…

RobotFramework 自动化测试实战进阶篇

工具 Robotframework, 采用PO设计模式 PO模型 PO模型即Page Objects&#xff0c;直译意思就是“页面对象”&#xff0c;通俗的讲就是把一个页面&#xff0c;或者说把一个页面的某个区域当做一个对象&#xff0c;通过封装这个对象可以实现调用。 PO设计的好处 代码复用&…

MAX10 ADC的一些基知识

MAX10 ADC 的一些知识 1、 MAX 10 内部集成的12bit SAR ADC的特点为&#xff1a; a、 采样速率高达1Mhz. b、 模拟通道多达18个&#xff0c;单个ADC多达17个&#xff0c;双ADC器件中有16个双功能ADC通道&#xff0c;2个专用的ADC。 c、 提供单端测…

Blazor University (42)JavaScript 互操作 —— 生命周期和内存泄漏

原文链接&#xff1a;https://blazor-university.com/javascript-interop/calling-dotnet-from-javascript/lifetimes-and-memory-leaks/生命周期和内存泄漏源代码[1]如果我们运行我们在从 Javascript 调用 .NET 中创建的应用程序并检查浏览器控制台窗口&#xff0c;我们会看到…

深入浅出聊布隆过滤器(Bloom Filter)

之前在网上看到过这么一段话&#x1f447; Data structures are nothing different. They are like the bookshelves of your application where you can organize your data. Different data structures will give you different facility and benefits. To properly use the …

WinForm(五)控件和它的成员

窗体无疑是WinForm的主角&#xff0c;每个窗体都是用一个class来承载&#xff0c;那么窗体的控件&#xff0c;就是类中的私有字段了。每个窗体有三个文件&#xff0c;两个.cs文件&#xff0c;是一个分部类&#xff0c;Designer.cs是自动生成的C#代码&#xff0c;一般是拖拽控件…

一文详解|增长那些事儿

目录 增长的背景 1.1 增长的定义 1.2 如何判断事物是否在增长 1.3 如何判断事物能否持续增长 如何进行增长 2.1 寻找增长机会点&#xff08;人的能力&#xff09; 2.1.1 发散与收剑找机会点 2.1.2 实验分析验证 2.1.3 增长洞察提取策略 2.1.4 如何找到大机会 2.2 设…

在MVC项目中使用Ninject

项目结构图&#xff1a; App_start文件夹中的文件是VS自己创建的&#xff0c;其中NinjectWebCommon类在创建之初并不存在。后面会再次提到&#xff01; 添加一个Home控制器。代码如下&#xff1a; using EssentialTools.Models; using Ninject; using System; using System.Col…

一文学会Autofac的基础操作:几种实现注册方式、3种注入方式、生命周期、AOP以及过滤器实现依赖注入...

前言&#xff1a;直接开干。使用Autofac进行服务注册实践&#xff1a;新建三个项目&#xff0c;分别是webapi项目 Wesky.Core.Autofac以及两个类库项目 Wesky.Core.Interface和Wesky.Core.Service。在Webapi项目下&#xff0c;引用Autofac的三个包&#xff1a;Autofac、Autofac…

JavaScript数组迭代方法(图解)

转载于:https://www.cnblogs.com/seanna/p/6724032.html

Rider调试ASP.NET Core时报thread not gc-safe的解决方法

新建了一个ASP.NET Core 5.0的Web API项目&#xff0c;当使用断点调试Host.CreateDefaultBuilder(args)时&#xff0c;进入该函数后查看中间变量的值&#xff0c;报错Evaluation is not allowed: The thread is not at a GC-safe point。在群里问了也没人回应&#xff0c;可能没…

The SDK platform-tools version ((23)) is too old to check APIs compiled with API 26;

好像是更新过啥SDK之后&#xff0c;项目一直在包名的那一行显示红线&#xff0c;不过是不报编译错误的&#xff0c;就是看着老扎心老扎心的&#xff0c;开始以为是指定的SDK版本的问题&#xff0c;修改后发现无效&#xff0c;最后找到方法解决&#xff1a; 打开SDK Manager ---…