数据过滤器

介绍

使用软删除模式是很常见的,该 模式用于实际上不从数据库中删除实体,而只是将其标记为“已删除”。如果实体被软删除,则不应将其意外检索到应用程序中。为了实现这一点,我们必须在我们选择实体的每个查询中添加一个SQL ,其中的条件如'IsDeleted = false'。这不仅乏味,而且更重要的是可忘记的任务。为了保持干燥,应该有一种自动方式来做到这一点。

ASP.NET Boilerplate提供了可用于根据某些规则自动过滤查询的数据过滤器有一些预定义的过滤器,但您也可以创建自己的过滤器。

预定义过滤器

ISoftDelete

此软删除过滤器用于在查询数据库时自动过滤(从结果中提取)已删除的实体。如果 必须软删除实体,则必须实现定义IsDeleted 属性ISoftDelete接口例:

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

    public virtual bool IsDeleted { get; set; }
}

一个实体实际上并没有从数据库中删除,取而代之的是, 请将isDeleted属性设置为true。当您使用IRepository.Delete 方法时,ASP.NET Boilerplate会自动完成此操作 (您可以手动将IsDeleted设置为true,但Delete方法是更自然和首选的方法)。

当您从数据库中获得实现ISoftDelete的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条件正确地添加到生成的SQL查询中。

何时启用ISoftDelete?

除非您明确禁用ISoftDelete过滤器,否则它始终处于启用状态。

旁注:如果您实现了 IDeletionAudited(扩展了ISoftDelete),那么删除它的删除时间和用户ID也会由ASP.NET Boilerplate自动设置。

IMustHaveTenant

如果要构建多租户应用程序并将所有租户数据存储在单个数据库中,您绝对不希望租户意外地看到其他租户的数据。在这种情况下,您可以实现IMustHaveTenant例:

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

    public string Name { get; set; }
}

IMustHaveTenant定义TenantId属性以区分不同的租户实体。ASP.NET Boilerplate默认使用 IAbpSession获取当前的TenantId,并自动过滤当前租户的查询。

何时启用IMustHaveTenant?

默认情况下启用IMustHaveTenant。

如果当前用户未登录到系统或当前用户是 主机用户(主机用户是可以管理租户和租户数据的上层用户),则ASP.NET Boilerplate会自动禁用 IMustHaveTenant过滤器。因此,可以将所有租户的所有数据检索到应用程序。请注意,这与安全性无关,您应该始终授权敏感数据!

IMayHaveTenant

如果租户主机共享实体类(这意味着实体对象可能由租户或主机拥有),则可以使用IMayHaveTenant过滤器。IMayHaveTenant接口定义TenantId但它是 可为空

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

    public string RoleName { get; set; }
}

一个值意味着这是一个主机实体,一个非空值意味着该实体由资租户,其中Id是TenantId。ASP.NET Boilerplate默认使用IAbpSession获取当前的TenantId。IMayHaveTenant过滤器不像IMustHaveTenant那样常见,但您可能需要它来用于主机和租户使用的常见权利类型

何时启用IMayHaveTenant?

除非您明确禁用IMayHaveTenant,否则它始终处于启用状态。

禁用过滤器

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

var people1 = _personRepository.GetAllList();

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

var people3 = _personRepository.GetAllList();

DisableFilter方法将一个或多个过滤器名称作为字符串。AbpDataFilters.SoftDelete是一个常量字符串,包含ASP.NET Boilerplate的标准软删除过滤器的名称。

people2还将包含已删除的人,而people1和people3将仅返回未删除的人。使用using语句,可以禁用范围中的过滤器如果您不使用使用状态,则过滤器将被禁用,直到当前工作单元结束,或直到您再次显式启用它。

您可以注入IUnitOfWorkManager并在示例中使用它。此外,如果您的类继承了一些特殊的基类(如ApplicationService,AbpController,AbpApiController ......),您可以使用CurrentUnitOfWork属性作为快捷方式。

关于using语句

如果在使用using语句调用DisableFilter方法时启用了筛选器,则会禁用筛选器。然后在using语句后自动重新启用它。如果在using语句之前已禁用过滤器,则DisableFilter不执行任何操作,即使在using语句之后,过滤器仍保持禁用状态。

关于多租户

您可以禁用租赁过滤器以查询所有租户数据。请注意,这仅适用于单数据库方法。如果为每个租户分配了数据库,则禁用过滤器无助于查询所有租户的所有数据,因为它们位于不同的数据库中,甚至位于不同的服务器上。有关详细信息,请参阅多租户文档

全局禁用过滤器

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

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

启用过滤器

您可以使用EnableFilter方法在工作单元中启用过滤器,该方法类似于(和相反)DisableFilter。EnableFilter还返回在using语句使用的一次性用语,以便在需要时自动重新禁用过滤器。

设置过滤器参数

过滤器可以是参数化的IMustHaveTenant过滤器是这些类型的过滤器的示例,因为当前租户的Id在运行时确定。对于此类过滤器,我们可以根据需要更改过滤器值。例:

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

以下是如何为IMayHaveTenant过滤器设置tenantId值的示例:

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

SetFilterParameter方法还返回IDisposable。因此,我们可以在using语句中使用它来自动恢复 using语句之后的旧值

SetTenantId方法

虽然您可以使用SetFilterParameter方法更改MayHaveTenant和MustHaveTenant过滤器的过滤器值,但有一种更好的方法可以更改租户过滤器:SetTenantId()SetTenantId更改两个过滤器的参数值,也适用于每个租户方法的单个数据库和数据库。强烈建议您使用SetTenantId 更改租赁过滤器参数值。有关详细信息,请参阅多租户文档

ORM集成

预定义过滤器的数据过滤适用于 NHibernateEntity Framework 6.x和Entity Framework Core目前,您只能为Entity Framework 6.x定义自定义过滤器。

实体框架

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

要为Entity Framework创建自定义过滤器并将其集成到ASP.NET Boilerplate中,首先我们需要定义一个接口,该接口将由使用此过滤器的实体实现。假设我们想要通过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方法中将此过滤器注册到ASP.NET Boilerplate的工作单元系统 

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

第一个参数是我们之前定义的相同唯一名称。第二个参数指示默认情况下是启用还是禁用此过滤器。在声明这样的参数滤波器之后,我们可以通过在运行时提供它来使用它。

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

我们可以从某个源获取personId而不是静态编码。以上示例适用于参数滤波器。过滤器可以具有零个或多个参数。如果没有参数,则无需设置过滤器参数值。此外,如果默认情况下启用它,则无需手动启用它(您始终可以禁用此功能)。

EntityFramework.DynamicFilters的文档

有关动态数据过滤器的更多信息,请参阅EF的github页面上的文档:https//github.com/jcachat/EntityFramework.DynamicFilters

可以为安全性,主动/被动实体等创建自定义过滤器。

其他ORM

对于Entity Framework Core和NHibernate,数据过滤在存储库 级别实现。这意味着它只在您查询存储库时进行过滤。

注意:如果直接使用DbContext(对于EF Core)或通过自定义SQL进行查询,则必须自己处理过滤。

nidie.com.cn - 用心与你沟通