• ABP中的数据过滤器


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

    一.预定义过滤器

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

    1.IDataFilter局部过滤

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

    namespace Acme.BookStore
    {
        public class MyBookService : ITransientDependency
        {
            private readonly IDataFilter _dataFilter;
            private readonly IRepository _bookRepository;
    
            public MyBookService(IDataFilter dataFilter, IRepository bookRepository)
            {
                _dataFilter = dataFilter;
                _bookRepository = bookRepository;
            }
    
            public async Task> GetAllBooksIncludingDeletedAsync()
            {
                // 临时禁用ISoftDelete过滤器
                using (_dataFilter.Disable())
                {
                    return await _bookRepository.GetListAsync();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

    2.AbpDataFilterOptions全局过滤

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

    Configure(options =>
    {
        options.DefaultStates[typeof(ISoftDelete)] = new DataFilterState(isEnabled: false);
    });
    
    • 1
    • 2
    • 3
    • 4

    这样就会全局地把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, IIsActive
    {
        public string Name { get; set; }
        public bool IsActive { get; set; } //Defined by IIsActive
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.重写DbContext中的方法

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

    protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled() ?? false;
    
    protected override bool ShouldFilterEntity(IMutableEntityType entityType)
    {
        if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity)))
        {
            return true;
        }
        return base.ShouldFilterEntity(entityType);
    }
    
    protected override Expression> CreateFilterExpression()
    {
        var expression = base.CreateFilterExpression();
    
        if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity)))
        {
            Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive");
            expression = expression == null ? isActiveFilter : CombineExpressions(expression, isActiveFilter);
        }
        return expression;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      突然看上去觉得这个自定义过滤器好复杂,后来想想那ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant)是如何实现的呢?然后就找到了源码ABP/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

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

    三.遇到的实际问题

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

    public async Task> GetAll(GetArticleInputDto input)
    {
        // 临时禁用掉IMultiTenant过滤器
        using (_dataFilter.Disable())
        {
            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>(items);
            return new PagedResultDto(totalCount, dto);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      对于"主中心中可以看到所有分中心的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

  • 相关阅读:
    springboot+影院售票小程序 毕业设计-附源码111154
    go操作Kfaka
    【趣味随笔】盘点那些国内外知名的扫地机器人品牌
    qt6 多媒体开发代码分析(四、视频播放)
    IDEA重新choose source
    Docker本地部署Drupal并实现公网访问
    JVM(宋红康)学习之运行时数据区之PC寄存器
    java毕业设计二手交易系统Mybatis+系统+数据库+调试部署
    第四代智能井盖传感器:智能井盖位移怎么进行监测
    1024程序员节——我是猿,我为自己带盐
  • 原文地址:https://blog.csdn.net/shengshengwang/article/details/126237744