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

Posted RAINAI

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP官方文档翻译 3.8 数据过滤器相关的知识,希望对你有一定的参考价值。

数据过滤器

介绍

  软删除模式是常用的模式,这种模式并没有从数据库中删除实体而是打上\'deleted\'的标记。所以,如果实体被软删除,那么它不应该被意外的提取到应用中。为了保证这种事情不会发生,当我们选择实体时,应该添加入‘IsDeleted=false’这样的SQL where条件。这是一个乏味、易忘但很重要的工作。所以,应该由自动的方式来完成。

  ABP提供了数据过滤器,可以用来基于一些规则自动过滤查询。这有一些预定义的过滤器,也可以创建自己的过滤器。

预定义过滤器

ISoftDelete

  Sofe-delete过滤器用来当查询数据库时自动过滤(从结果中提取)已删除的实体。如果一个实体为软删除模式的,它必须实现ISoftDelete接口,这个接口只有IsDeleted属性。示例:

public class Person : Entity, ISoftDelete
{
    public virtual string Name { get; set; }

    public virtual bool IsDeleted { get; set; }
}

  Person实体不会真正从数据库中删除,而是当需要删除它时,将IsDeleted属性设置为true。当使用IRepository.Delete方法(你可以手动设置IsDeleted为true,但是Delete方法是更自然、更好的方式)时,ABP自动完成这个操作。

  实现ISoftDelete接口后,当你从数据库获取People的列表时,已删除的people不会被提取。这里有一个使用person仓储类获取所有people的示例类:

public class MyService
{
    private readonly IRepository<Person> _personRepository;

    public MyService(IRepository<Person> personRepository)
    {
        _personRepository = personRepository;
    }

    public List<Person> GetPeople()
    {
        return _personRepository.GetAllList();
    }
}

  GetPeople方法仅获取Isdeleted=false(没有删除)的person实体。所有的仓储方法和导航属性都可以正常工作。我们可以添加一些其他的Where条件。它会自动添加IsDeleted=false条件到生成的查询语句中。

何时使用?

  ISoftDelete过滤器除非显示的禁用它,否则它总是启用的。

  边注:如果你实现了IDeletionAudited(它扩展了ISoftDelete)接口,ABP会自动设置删除时间和删除用户id。

IMustHaveTenant

  如果你创建了多租户的应用,并把所有的租户数据都存放在一个数据库中,你肯定不想一个租户可以看到其他租户的数据。在这种情况下,你可以实现IMustHaveTenant接口。示例:

public class Product : Entity, IMustHaveTenant
{
    public int TenantId { get; set; }

    public string Name { get; set; }
}

  IMustHaveTenant定义了TenantId用来区分不用租户的实体。ABP默认使用IAbpSession获取当前租户的TenantId,并自动为当前租户过滤查询。

何时使用?

  IMustHaveTenant默认是启用的。

  如果当前用户没有登录到系统或者当前用户是一个租主用户(租户用户是超级用户,可以管理租户和租户数据),ABP自动禁用IMustHaveTenant过滤器。因此,所有租户的所有数据都被提取到应用中。注意这个和安全无关,你应该总是授权敏感的数据。

IMayHaveTenant

  如果实体被租户和租主共享(这意味着一个实体对象可能被一个租户拥有,也可能是租主)。你可以使用IMayHaveTenant过滤器。IMayHaveTenant接口定义了TenantId,但它是nullable。

public class Role : Entity, IMayHaveTenant
{
    public int? TenantId { get; set; }

    public string RoleName { get; set; }
}

  null值意味着是一个租主实体,非null值以为了这个实体数据租户且租户id为TenantId。ABP默认使用IAbpSession获取当前租户的Id。IMayHaveTenant接口不如IMustHaveTenant常见。但是当需要被租户和租主同时使用数据时,你就会需要它。

何时使用

  IMayHaveTenant总是可用的,除非你显示的禁用它。

禁用过滤器

  你可以通过调用DisableFilter方法为每个工作单元禁用过滤器,如下所示:

var people1 = _personRepository.GetAllList();

using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
{
    var people2 = _personRepository.GetAllList();                
}

var people3 = _personRepository.GetAllList();

  DisableFilter方法使用一个或多个过滤器的名字作为参数字符串。AbpDataFilters.SoftDelete是一个常量字符串,它包含了ABP标准软删除过滤器的名字。

  people2将包含删除的people,但people1和people3只有未删除的people。使用using语句,你可以在一个范围内禁用过滤器。如果你不使用using语句,过滤器将禁用到当前工作单元的结尾或者你重新显示的启用它。

  你可以注入IUnitOfWorkManager并如在本例中那样使用。你也可以使用CurrentUnitOfWork属性作为捷径,如果你的类集成了一些特殊的基类(如ApplicationService、AbpController、AbpApiController...)。

关于using语句

  当你使用using语句调用DisableFilter方法时,如果这时过滤器可用的,那么调用方法之后过滤器是禁用的,using语句之后会自动重新启用过滤器。但是,如果在using语句之前过滤器已经是禁用的,那么DisableFilter方法实际上什么也没做,using语句之后过滤器仍然保持禁用。

关于多租户

  你可以禁用租户过滤器来查询所有租户的数据。但是记住,这种方法只对单数据库方式有效。如果每一个租户都有单独的数据库,禁用过滤器将不会查询所有租户的所有数据,因为他们在不同的数据库中,甚至在不同的服务器上。参见多租户文档了解更多信息。

全局禁用过滤器

  如果需要,可以全局禁用预定义的过滤器。例如,全局禁用软删除过滤器,在你模块的PreInitialize方法中添加下面的代码:

Configuration.UnitOfWork.OverrideFilter(AbpDataFilters.SoftDelete, false);

启用过滤器

  你可以在工作单元里使用EnableFilter方法来启用过滤器,和DisableFilter相似(功能相反)。EnableFilter方法返回disposable用来在using语句中使用,如果需要,可以自动重新禁用过滤器。

设置过滤器参数

  过滤器可以是参数化的。IMustHaveTenant过滤器就是这种类型的一个示例,因为当前租户id是在运行时决定的。对于这种过滤器,如果需要的话我们可以改变过滤器的值,例如:

CurrentUnitOfWork.SetFilterParameter("PersonFilter", "personId", 42);

  另一个示例:设置IMayHaveTenant过滤器的tenantId值:

urrentUnitOfWork.SetFilterParameter(AbpDataFilters.MayHaveTenant, AbpDataFilters.Parameters.TenantId, 42);

  SetFilterParameter方法也返回一个IDisposable接口。所以,我们可以使用using语句自动重建之前的值,在using语句之后。

SetTenantId方法

  当你使用SetFilterParameter方法改变MayHaveTenant和MustHaveTenant过滤器的值时,这有一个更好的方式更改租户过滤器:SetTenantId()。SetTenantId为这两个过滤器更改参数值,并且对单数据库和每个租户一个数据库两种类型都有效。所以,建议使用SetTenantId改变租户过滤器的参数值。参见多租户文档了解更多信息。

ORM集成

  预定义过滤器对NHibernate、EntityFramework 6.x和Entity Framework Core同样有效。目前,你只能为Entity Framework 6.x定义自定义过滤器。

Entity Framework

  对Entity Framework集成,使用EntityFramework.DynamicFilters类库实现自动数据过滤。

  为了为Entity Framework创建一个自定义过滤器并集成到ABP,首先我们应该定义一个接口,它将被使用这个过滤器的实体实现。假定,我们想自动通过PersonId过滤实体。示例接口:

public interface IHasPerson
{
    int PersonId { get; set; }
}

  然后我们可以为需要的实体实现这个接口。示例实体如下:

public class Phone : Entity, IHasPerson
{
    [ForeignKey("PersonId")]
    public virtual Person Person { get; set; }
    public virtual int PersonId { get; set; }

    public virtual string Number { get; set; }
}

  我们使用它的规则定义过滤器。在我们的DbContext类,我们重写OnModelCreating并定义过滤器,如下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Filter("PersonFilter", (IHasPerson entity, int personId) => entity.PersonId == personId, 0);
}

  “PersonFilter”是过滤器唯一的名称。第二个参数定义了过滤器接口和personId过滤器参数(如果过滤器没有参数可以不需要),最后的参数是personId的默认值。

  最后一件事情,我们必须在模块的PreInitialize方法中注册这个过滤器到ABP的工作单元系统中。

Configuration.UnitOfWork.RegisterFilter("PersonFilter", false);

  第一个参数是和我们之前定义的同样的名字。第二个参数标示这个过滤器默认是启用还是禁用。声明这么一个参数化过滤器之后,我们可以在运行时提供给他参数值并使用它。

using (CurrentUnitOfWork.EnableFilter("PersonFilter"))
{
    using(CurrentUnitOfWork.SetFilterParameter("PersonFilter", "personId", 42))
    {
        var phones = _phoneRepository.GetAllList();
        //...
    }
}

  我们可以从一些源获取到personId而不是静态编码。上面的例子是参数化过滤器的。过滤器可以没有或有更多的参数。如果没有参数就不需要设置过滤器参数值。如果默认是启用的就不需要手动启用它(当然,我们可以禁用它)。

EntityFramework.DynaamicFilters文档

  更过关于动态数据过滤器的信息,参见github页上的文档:https://github.com/jcachat/EntityFramework.DynamicFilters

  我们可以为安全、激活/失活实体等等创建自定义过滤器。

其他ORMs

  对于Entity Framework Core和NHibernate,数据过滤是在仓储级别实现的。这就意味着,它只有当你通过仓储查询时才会过滤。如果你直接使用DbContext(对于 EF Core)或通过自定义SQL,你需要自己处理过滤。

Configuration.UnitOfWork.OverrideFilter(AbpDataFilters.SoftDelete, false);


返回主目录

以上是关于ABP官方文档翻译 3.8 数据过滤器的主要内容,如果未能解决你的问题,请参考以下文章

ABP官方文档翻译 3.5 规约

ABP官方文档翻译 3.5 规约

ABP官方文档翻译 3.3 仓储

ABP官方文档翻译 3.3 仓储

ABP官方文档翻译 1.2 N层架构

ABP官方文档翻译 3.1 实体