本文首先介绍了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.cs



看了源码实现后会发现格式一模一样,所以自定义过滤器使用起来没有这么复杂。

三.遇到的实际问题

  假如在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

ABP中的数据过滤器的更多相关文章

  1. 文章翻译:ABP如何在EF core中添加数据过滤器

    原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...

  2. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  3. ABP理论学习之数据过滤器

    返回总目录 本篇目录 介绍 预定义过滤器 关闭过滤器 开启过滤器 设置过滤器参数 定义自定义过滤器 其他ORM 介绍 软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除 ...

  4. ABP 配置全局数据过滤器

    ABP官方数据过滤的地址:https://aspnetboilerplate.com/Pages/Documents/Data-Filters 中文可以看这个:https://aspnetboiler ...

  5. ABP 配置全局数据过滤器 II

    第一篇 那种写法有些复杂, 简单办法是直接注入 切换到 ***.EntityFramework 项目 在Uow 里面创建 ***EfUnitOfWork.cs 类 public class Coope ...

  6. ABP官方文档翻译 3.8 数据过滤器

    数据过滤器 介绍 预定义过滤器 ISoftDelete 何时使用? IMustHaveTenant 何时使用? IMayHaveTenant 何时使用 禁用过滤器 关于using语句 关于多租户 全局 ...

  7. ABP的数据过滤器(Data Filters)

    http://www.aspnetboilerplate.com/Pages/Documents/Data-Filters 我们在数据库开发中,一般会运用软删除 (soft delete)模式 ,即不 ...

  8. ABP框架使用Oracle数据库,并实现从SQLServer中进行数据迁移的处理

    ABP框架的数据访问底层是基于EFCore(Entity Framework Core)的,是微软标志性且成熟的ORM,因此它本身是支持多种主流数据库MySQL,SqlServer,Oracle,SQ ...

  9. ABP框架之——数据访问基础架构(下)

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. EF Core集成 EF Core是微软的ORM,可以使用它与主流的数据库提供商 ...

随机推荐

  1. 用python实现输入三边判断能否组成三角形

    # -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'sanjiaoxing.py'## Creat ...

  2. Git移除远程已经上传的文件

    我们常常会将本地的一些秘钥文件不小心推送到远端,此时仅仅修改本地的.gitignore文件,然后再提交推送是不能将远端的此文件删除的. 此时可以用下面的命令 git rm --cached filen ...

  3. 每天一个 HTTP 状态码 203

    203 Non-Authoritative Information 203 Non-Authoritative Information 'Non-Authoritative Informative' ...

  4. ex_Lucas定理

    Lucas定理(p为质数): \(C_n^m=C_{n/p}^{m/p}*C_{n\ mod\ p}^{m\ mod\ p}\) 可是p不为质数怎么办呢? ex_Lucas定理 (p不为质数) 思路 ...

  5. Kafka消息的压缩机制

    最近在做 AWS cost saving 的事情,对于 Kafka 消息集群,计划通过压缩消息来减少消息存储所占空间,从而达到减少 cost 的目的.本文将结合源码从 Kafka 支持的消息压缩类型. ...

  6. AR Engine运动跟踪能力,高精度实现沉浸式AR体验

    随着电子产品的普遍应用,AR技术也开始广泛普及,在游戏.电商.家装等领域都有涉及.比如,在室内设计时,我们可以通过AR技术在实际场景中进行虚拟软装的搭配,运用华为AR Engine运动跟踪能力在实际应 ...

  7. 彰显个性│制作一个独一无二的动态 svg 头像

    一.头像预览 看一下博主的动态图像,是不是很炫酷,想不想拥有一个? 这是一个 svg 图片,svg 图片不仅可以通过制图软件制作外,其实也可以通过代码进行开发 因为 svg 本质上是一个下 xml 文 ...

  8. 【Redis】集群数据迁移

    Redis通过对KEY计算hash,将KEY映射到slot,集群中每个节点负责一部分slot的方式管理数据,slot最大个数为16384. 在集群节点对应的结构体变量clusterNode中可以看到s ...

  9. 这就是艺术「GitHub 热点速览 v.22.25」

    作者:HelloGitHub-小鱼干 不知道写了那么久代码的你,是否还记得"代码写诗"这个词,它是用来形容代码的优雅.但是本周的项目,虽然你看到的是代码的成品,也会惊讶于它的艺术感 ...

  10. 如何使用lerna进行多包(package)管理

    为什么要用lerna 将大型代码仓库分割成多个独立版本化的 软件包(package)对于代码共享来说非常有用.但是,如果某些更改 跨越了多个代码仓库的话将变得很 麻烦 并且难以跟踪,并且, 跨越多个代 ...