ABP vNext详细教程——数据过滤器
目录
简介
基础用法
2、使用
3、查询拼装
4、禁用
5、补充说明
源码解析
1、DataFilter
2、AbpDbContext
简介
数据过滤器是ABP vNext的重要功能,在ABP vNext中,软删除、多租户都是以数据过滤器为基础实现的。
这一篇,我将从数据过滤器用法、原理等方面详细介绍数据过滤器。
基础用法
1、定义
对于数据过滤器的定义非常简单,其形式是C#中基础的接口。
这里我们以ABP的软删除过滤器为例,其源代码如下:
namespace Volo.Abp;/// <summary>
/// Used to standardize soft deleting entities.
/// Soft-delete entities are not actually deleted,
/// marked as IsDeleted = true in the database,
/// but can not be retrieved to the application normally.
/// </summary>
public interface ISoftDelete
{/// <summary>/// Used to mark an Entity as 'Deleted'./// </summary>bool IsDeleted { get; }
}
可以看到,软删除过滤器是一个非常简单的接口,ABP未给数据过滤器提供更为基础的接口。
在数据过滤器接口定义中,我们需要声明用于数据过滤 ISoftDelete 接口中,数据过滤条件为 IsDeleted ,当值为 true 时,表示数据被软删除。正常查询时该数据不会被查询到,但数据并未在数据库中物理删除,如果禁用数据过滤器
2、使用
当我们需要在某个实体中使用数据过滤器时,只需要继承自数据过滤器接口并实现其中的方法即可,例如使用软删除过滤器时,实体定义可参考以下代码:
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities;namespace Demo;
public class Book : Entity<Guid>, ISoftDelete
{public string Name { get; set; }
public bool IsDeleted { get; set; }
}
在ABP中默认已提供了两种数据过滤器:ISoftDelete 和 IMultiTenant , IMultiTenant 用于多租户,依据不同租户ID只查询该租户下的数据。
3、查询拼装
在我们定义数据过滤器后,需要对DbContext的代码进行扩展,首先以ABP官方文档中的代码为例:
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;
}
该代码可以用于我们项目中自定义的DbContext中,通常继承自 AbpDbContext 。
IsActiveFilterEnabled 用于获取当前过滤器是否被禁用。
ShouldFilterEntity 用于判定某个实体是否使用了数据过滤器,其中 IIsActive 为该Demo中用到的自定义数据过滤器接口,当某个实体实现了任意一个数据过滤器接口时,该接口都返回 true , base.ShouldFilterEntity<TEntity>(entityType) 中包含了ABP在AbpDbContext中已定义的对 ISoftDelete 和 IMultiTenant 的处理
CreateFilterExpression 用于拼装查询条件的表达式目录树,相当于如果启用指定数据过滤器,则在查询时强制拼装一个where条件, var expression = base.CreateFilterExpression<TEntity>(); 这一句用于获取ABP原有的表达式目录树,其中已拼装了对软删除和多租户的数据过滤表达式。
4、禁用
正常情况下,完成上面三个步骤后我们只需要正常在应用中使用数据查询即可,不需要增加任何代码,数据查询时会自动使用数据过滤器的查询条件过滤数据。
但某些情况下,我们可能希望禁用数据过滤器查询到全部数据,例如我们希望同时查到已有数据和被软删除的数据,这样我们就需要在某个逻辑代码中禁用 ISoftDelete 过滤器。例如以下代码:
public class MyBookService : ITransientDependency
{private readonly IDataFilter<ISoftDelete> _softDeleteFilter;private readonly IRepository<Book, Guid> _bookRepository;public MyBookService(IDataFilter<ISoftDelete> softDeleteFilter,IRepository<Book, Guid> bookRepository){_softDeleteFilter = softDeleteFilter;_bookRepository = bookRepository;}public async Task<List<Book>> GetAllBooksIncludingDeletedAsync(){//Temporary disable the ISoftDelete filterusing (_softDeleteFilter.Disable()){return await _bookRepository.GetListAsync();}}
}
需要禁用某个数据过滤器时,我们只需要注入 IDataFilter 接口,并在需要禁用时使用 using (_softDeleteFilter.Disable()) ,在此using的作用域中,该数据过滤器就会被禁用,我们就可以跳过数据过滤查询到所有数据。
5、补充说明
在ABP中,我们如果使用软删除过滤器或者多租户过滤器,使用2.2中的方法,给实体实现数据过滤器接口即可,其他步骤在ABP中已进行封装。
如果我们的实体继承自 FullAuditedEntity 或 FullAuditedAggregateRoot ,则其中已包含对软删除接口的实现,不需要额外实现ISoftDelete接口。
如果需要对包含软删除接口的数据进行物理删除,则直接使用ABP仓储中的 HardDeleteAsync 方法即可。
源码解析
1、DataFilter
ABP和数据过滤器相关的主要代码存放于Volo.Abp.Data类库中。主要涉及以下类:
IDataFilter:数据过滤器启停接口,用于在2.4章节中有使用到,可在using作用域下对指定数据过滤器进行启用和停用。
DataFilter:IDataFilter接口的默认实现类。
DataFilterState:用于定义数据过滤器状态。
AbpDataFilterOptions:可以通过Operation参数配置的方式配置数据过滤器的启停。
DataFilterState源代码如下:
public class DataFilterState
{public bool IsEnabled { get; set; }public DataFilterState(bool isEnabled){IsEnabled = isEnabled;}public DataFilterState Clone(){return new DataFilterState(IsEnabled);}
}
该类的核心是IsEnabled,true和false分别代表数据过滤器启用和停用。
AbpDataFilterOptions源代码如下:
public class AbpDataFilterOptions
{public Dictionary<Type, DataFilterState> DefaultStates { get; }public AbpDataFilterOptions(){DefaultStates = new Dictionary<Type, DataFilterState>();}
}
该类使用字典配置数据过滤器的默认启停状态。
IDataFilter接口源码如下:
public interface IDataFilter<TFilter>where TFilter : class
{IDisposable Enable();IDisposable Disable();bool IsEnabled { get; }
}public interface IDataFilter
{IDisposable Enable<TFilter>()where TFilter : class;IDisposable Disable<TFilter>()where TFilter : class;bool IsEnabled<TFilter>()where TFilter : class;
}
可以看到,ABP定义了泛型和非泛型两种方式的IDataFilter接口。泛型接口对每种数据过滤器单独注册依赖实例,非泛型接口统一注册一个实例用于管理所有数据过滤器的启停。
Enable和Disable方法分别用于启用和停用指定数据过滤器,IsEnabled属性用于获取指定数据过滤器是否可用。
DataFilter类源代码如下:
public class DataFilter : IDataFilter, ISingletonDependency
{private readonly ConcurrentDictionary<Type, object> _filters;private readonly IServiceProvider _serviceProvider;public DataFilter(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;_filters = new ConcurrentDictionary<Type, object>();}public IDisposable Enable<TFilter>()where TFilter : class{return GetFilter<TFilter>().Enable();}public IDisposable Disable<TFilter>()where TFilter : class{return GetFilter<TFilter>().Disable();}public bool IsEnabled<TFilter>()where TFilter : class{return GetFilter<TFilter>().IsEnabled;}private IDataFilter<TFilter> GetFilter<TFilter>()where TFilter : class{return _filters.GetOrAdd(typeof(TFilter),factory: () => _serviceProvider.GetRequiredService<IDataFilter<TFilter>>()) as IDataFilter<TFilter>;}
}public class DataFilter<TFilter> : IDataFilter<TFilter>where TFilter : class
{public bool IsEnabled {get {EnsureInitialized();return _filter.Value.IsEnabled;}}private readonly AbpDataFilterOptions _options;private readonly AsyncLocal<DataFilterState> _filter;public DataFilter(IOptions<AbpDataFilterOptions> options){_options = options.Value;_filter = new AsyncLocal<DataFilterState>();}public IDisposable Enable(){if (IsEnabled){return NullDisposable.Instance;}_filter.Value.IsEnabled = true;return new DisposeAction(() => Disable());}public IDisposable Disable(){if (!IsEnabled){return NullDisposable.Instance;}_filter.Value.IsEnabled = false;return new DisposeAction(() => Enable());}private void EnsureInitialized(){if (_filter.Value != null){return;}_filter.Value = _options.DefaultStates.GetOrDefault(typeof(TFilter))?.Clone() ?? new DataFilterState(true);}
}
DataFilter同样包含泛型方式和非泛型方式,分别实现泛型和非泛型方式的IDataFilter接口。
DataFilter在ABP中被注册为单例,注册位置为Volo.Abp.Data中的AbpDataModule。
在泛型方式下每一种数据过滤器对应一个实例,可以看到核心是对_filter对象的操作,该对象包含DataFilterState状态并通过AsyncLocal实现异步代码间的对象共享。
在EnsureInitialized方法中可以看到,DataFilter方式启停优先级大于AbpDataFilterOptions配置,数据过滤器默认状态为启用。
泛型方式DataFilter是通过_filters字典对所有数据过滤器进行管理,_filters是一个字典,Key为数据权限类型,Value是该类型对应的泛型方式DataFilter对象。
2、AbpDbContext
DataFilter在EF中使用主要在AbpDbContext中,其中变量定义涉及以下三行代码:
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
第一行用于获取DataFilter实例,第二行用于判定多租户过滤器是否启用,第三行用于判定软删除过滤器是否启用。
在AbpDbContext中提供了对多租户过滤器和软删除过滤器的表达式目录树拼装,代码如下:
protected virtual Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()where TEntity : class
{Expression<Func<TEntity, bool>> expression = null;if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))){expression = e => !IsSoftDeleteFilterEnabled || !EF.Property<bool>(e, "IsDeleted");}if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))){Expression<Func<TEntity, bool>> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property<Guid>(e, "TenantId") == CurrentTenantId;expression = expression == null ? multiTenantFilter : CombineExpressions(expression, multiTenantFilter);}return expression;
}
在 BasicRepositoryBase 中,同样注入了DataFilter对象,在ABP源码中该对象主要用于MongoDB操作,其原理和AbpDbContext相同。如果我们需要自定义仓储,也可以使用BasicRepositoryBase中的DataFilter对象。
原文地址
ABP vNext详细教程——数据过滤器相关推荐
- ABP vNext初始化种子数据
1. 定义实体类 public class Book : AuditedAggregateRoot<Guid>{public string Name { get; set; }public ...
- ABP vNext 实现租户Id自动赋值插入
背景 在使用ABP vNext过程中,因为我们的用户体系庞大,所以一直与其他业务同时开发,在开发其他业务模块时,我们一直存在着误区:认为ABP vNext 自动处理了数据新增时的租户Id(Tenant ...
- ABP vNext微服务架构详细教程——结束语
ABP vNext微服务架构详细教程--简介 ABP vNext微服务架构详细教程--架构介绍 ABP vNext微服务架构详细教程--身份管理服务 ABP vNext微服务架构详细教程--基础服务层 ...
- ABP vNext微服务架构详细教程——项目部署
1 基础配置 在之前的文章中,我们已经配置了Kubernetes集群并安装了管理工具Kubesphere,文章地址为:https://mp.weixin.qq.com/s/MgpdMv5A-fYxN7 ...
- ABP vNext微服务架构详细教程——架构介绍
总体架构 所有应用服务.API网关.身份认证服务均部署在Kubernetes容器中,由Kubernetes提供应用配置.服务治理.服务监控等功能. 客户端所有访问均通过Kubernetes的Nginx ...
- ABP vNext微服务架构详细教程——分布式权限框架(上)
1 简介 ABP vNext框架本身提供了一套权限框架,其功能非常丰富,具体可参考官方文档:https://docs.abp.io/en/abp/latest/Authorization 但是我们使用 ...
- ABP vNext微服务架构详细教程——身份管理服务
1 框架搭建 ABP vNext创建包含app和module两种模板,其中app方式所创建的模板包含用户.角色.权限管理,ABP基础配置IdentityServer的基础配置数据等功能.module模 ...
- ABP vNext微服务架构详细教程——基础服务层
1 服务创建 在除身份管理相关服务以外的其他业务服务中,我们不需要包含用户角色权限管理功能模块,ABP vNext框架为我们提供了模块模式,其默认模板不包含身份管理相关模块,更适合用于搭建普通的业务微 ...
- ABP vNext微服务架构详细教程——简介
简介 该系列文章主要展示ABP vNext框架在微服务架构下的用法,提供一套可落地的技术实现思路,并演示各服务在Kubernetes下的部署方案. 基础概念 ABP vNext 基于ASP.NET C ...
最新文章
- winScp中文乱码设置
- Mybatis从入门到精通下篇
- SpringBoot实战教程(7)| 整合JPA
- Xshell连接VMware的linux系统
- 苹果鸠占鹊巢打败微软 纳德拉欲以其人之道还治其身
- 项目Alpha冲刺Day3
- java 线程池 hash_java线程池实例 - Hashsound的个人空间 - OSCHINA - 中文开源技术交流社区...
- Tests of the Equality of Two Means
- python人脸对比相似度_相似度算法原理及python实现
- linux i217 v网卡驱动,英特尔网卡驱动下载_Intel英特尔I217I218I219系列网卡驱动官方下载 - 系统之家...
- java中图片排版_基于Java的图像排版系统的设计.pdf
- 还不会用 Python 提取 PDF 表格?三种类型数据,轻松转换成 Excel
- 笨笨的Zend - RewriteBase
- blend2d + MFC
- 计算机辅助出版的英文缩写是,计算机辅助设计的英文缩写为
- 关于射线检测与碰撞检测
- Vue axios请求自带域名,接口及项目名
- GitHub牛逼开源项目!像写 Markdown 一样画流程图
- 江湖救急(处理域名未备案网站问题)
- “高通”字库芯片的使用方法
热门文章
- springboot乐校园二手书交易管理系统
- Dell XPS 9360 安装debian10---安装后
- Error: PostCSS plugin postcss-discard-comments requires PostCSS 8
- EXCEL数据有效性-允许序列-数据来源如何设置为其它工作表的一大连续区
- RuoYiApplication: Failed to retrieve application JMX service URL解决方法
- 百人计划(图形部分)移动端TB(D)R架构基础
- uniApp 状态栏字体颜色
- linux用wubi安装ubuntu,硬盘Wubi方式安装Ubuntu Linux体验
- android性能优化面试,Android面试心得必备技能储备详解
- C# 最佳做法--- C# 中 SOLID 原则