ABP vNext详细教程——数据过滤器

1136 篇文章 56 订阅
635 篇文章 16 订阅
文章详细介绍了ABPvNext框架中的数据过滤器,包括基础用法如软删除和多租户过滤,如何查询拼装过滤条件,以及如何禁用过滤器。同时,解析了相关源码,展示了DataFilter和AbpDbContext在数据过滤中的实现细节。
摘要由CSDN通过智能技术生成

 

目录

简介

基础用法

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 filter
    using (_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对象。

原文地址 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值