点这里进入ABP系列文章总目录

基于DDD的现代ASP.NET开发框架--ABP系列之13、ABP领域层——数据过滤器(Data filters)

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。

ABP的官方网站http://www.aspnetboilerplate.com

ABP在Github上的开源项目https://github.com/aspnetboilerplate


介绍

在数据库开发中,我们一般会运用软删除(soft-delete)模式,即不直接从数据库删除数据,而是标记这笔数据为已删除。因此,如果实体被软删除了,那么它就应该不会在应用程序中被检索到。要达到这种效果,我们需要在每次检索实体的查询语句上添加SQL的Where条件IsDeleted = false。这是个乏味的工作,但它是个容易被忘掉的事情。因此,我们应该要有个自动的机制来处理这些问题。

ABP提供数据过滤器(Data filters),它使用自动化的,基于规则的过滤查询。ABP已经有一些预定义的过滤器,当然也可以自行创建你专属的过滤器。

注意:只针对EntityFramework:ABP数据过滤器仅实现在EntityFramework。还无法在其它ORM工具中使用。见其它ORM章节于本文末端。

预定义过滤器

软删除接口(ISoftDelete)

软删除过滤器(Soft-delete filter )会过滤从数据库查询出来的实体且是自动套用(从结果集中提取出来)。如果实体需要被软删除,它需要实现ISoftDelete接口,该接口仅定义了一个IsDeleted属性。例:

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

Person实体实际上并没有从数据库中被删除,当删除此实体时,IsDeleted属性值会被设定为true。当你使用IRepository.Delete方法时,ABP会自动完成这些工作(你可以手动设定IsDeleted为true,但是Delete方法更加自然且是较建议的方式)。

当实现了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方法仅取得Person实体,该实体其IsDeleted = false(非删除状态)。所有的仓储方法以及导航属性都能够正常运作。我们可以添加一些其它的Where条件,Join...等等。它将会自动地添加IsDeleted=false条件到生成的SQL查询语句中。

注意:何时启用?ISoftDelete过滤器总是启用,除非你直接禁用它。

提醒:如果你实现了IDeletionAudited接口(该接口继承自ISoftDelete),删除创建时间和被删除的用户Id,这些都会由ABP进行自动的处理。

多租接口(IMustHaveTenant)

如果你创建一个多租户的应用程序(储存所有租户的数据于单一一个数据库中),你肯定不会希望某个租户看到其它租户的资料。你可以实现IMustHaveTenant接口于此案例中,示例如下:

   public class Product : IMustHaveTenant {
public virtual int TenantId { get; set; } public virtual string Name { get; set; }
}

IMustHaveTenant定义了TenantId来区别不同的租户实体。ABP使用IAbpSession来取得当前TenantId并且自动地替当前租户进行过滤查询的处理。

注意:何时启用?IMustHaveTenant默认是启用的。如果当前使用并没有登入到系统或是当前用户是一个管理级使用者(管理级使用者即为一个最高权限的使用者,它可以管理所有租户和租户的资料),ABP会自动地禁用IMustHaveTenant过滤器。因此,所有的租户的数据都可以被应用程序所检索。注意,这与安全性无关,你应该要对敏感数据进行验证授权处理。

多租接口(IMayHaveTenant)

如果一个实体类由多个租户(tenant)以及管理级使用者(host)所共享(这意味着该实体对象或许由租户(tenant)或是管理级使用者(host)所掌控),你可以使用IMayHaveTenantfilter。IMayHaveTenant接口定义了TenantId但是它是可空类(nullable)。

   public class Product : IMayHaveTenant {
public virtual int? TenantId { get; set; } public virtual string Name { get; set; }
}

当为null值,则代表这是一个管理级使用者(host)所掌控的实体,若为非null值,则代表这个实体是由租户(tenant)所掌控,而其Id值即为TenantId。ABP使用IAbpSession接口来取得当前TenantId。IMayHaveTenant过滤器并不如IMustHaveTenant普遍常用。但是当作为管理级使用者(host)和租户(tenant)所需要的通用结构使用时,你或许会需要用到它。

何时启用?IMayHaveTenant接口总是启用的,除非你直接禁用它。

禁用过滤器

可以在工作单元(unit of work)中调用DisableFilter方法来禁用某个过滤器,如下所示:

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

DisableFilter方法取得一或多个过滤器名称,且类型皆为string。AbpDataFilters.SoftDelete是一个常数字符串其包含了ABP标准的软删除过滤器。

people2亦可取得已标记为删除的People实体,而people1和people3将会是唯一的非已标记为删除的People实体。若配合使用using语法,你可以禁用其控制范围内(Scope)的过滤器。如果你不使用 using 语法 ,此过滤器会被一直禁用,直到工作单元(unit of work)结束或者再度启用它。(意思是:如果你使用"using"关键字声明,过滤器是启用状态;当前工作单元(unit of work)结束后,过滤器是禁止状态。如果不使用"using"关键字声明,默认过滤器是禁用状态,此时可以手动启用过滤器。)

你可以注入IUnitOfWorkManager并且在上述示例中使用。同样的,你可以使用CurrentUnitOfWork属性作为一个在应用服务中的简便方式(它是从ApplicationService类继承而来的)。

注意:关于using语法:假如过滤器在你调用DisableFilter方法并配合using语法之前已是启用,则过滤器会被禁用,并且会自动地在using语法结束后在度启用。但是若过滤器在using语法之前就已经被禁用了,DisableFilter方法实际上并不做任何式,并且过滤器会维持禁用状态即便是using语法的结束后。

启用过滤器

你可以在工作单元(unit of work)中使用EnableFilter方法启用过滤器,如同DisableFilter方法一般(两者互为正反两面)。EnableFilter亦会返回disposable来自动地重新禁用过滤器。

设定过滤器参数

过滤器可以被参数化(parametric)。IMustHaveTenant过滤器是这类过滤器的一个范本,因为当前租户(tenant)的Id是在执行时期决定的。对于这些过滤器,如果真有需要,我们可以改变过滤器的值。举例如下:

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

另一个示例如下:设定IMayHaveTenant过滤器的tenantId值:

CurrentUnitOfWork.SetfilterParameter(AbpDataFilters.MayHaveTenant, AbpDataFilters.Parameters.TenantId, );

自定义过滤器

欲创建定制的过滤器并且整合到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; }
}

因为ABP使用EntityFramework.DynamicFilters这个过滤器,我们使用它的规则(rule)来定义过滤器。在我们的DbContext类中,我们重写了OnModelCreating并且定义了过滤器如下示例所示:

  protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Filter("PersonFilter", (IHasPerson entity, int personId) => entity.PersonId == personId, );
}

PersonFilter过滤器在这里是一个唯一的过滤器名称。再来就是过滤器接口的参数定义和personId过滤器参数(不一定需要,假如过滤器是属于不可参数化(parametric)型),最后一个参数为personId的默认值。

最后一个步骤,我们需要注册这个过滤器到ABP工作单元(unit of work)系统中,设定的位置在我们模块里的PreInitialize方法。

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

第一个参数是我们刚刚所定义的唯一名称,第二个参数指示这个过滤器预设是启用还是禁用。在声明完这些可参数化(parametric)的过滤器后,我们可以在执行期间指定它的值来操作这个过滤器。

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

我们可以从有些数据源中取得personId而不需要写死在程序代码中。上述示例是为了要能够程序化过滤器。过滤器可拥有0到更多的参数。假如是无参数的过滤器,它就不需要设定过滤器的值。同样地,假如它预设是启用,就不需要手动启用(当然的,我们也可以禁用它)。

EntityFramework.DynamicFilters的文件:若需要更多关于动态数据过滤器的相关信息,可以见其在git上的文件https://github.com/jcachat/EntityFramework.DynamicFilters

我们可以为安全性创建一个定制化的过滤器,主/被动实体,多租户...诸如此类的。

其它对象关系映射工具

ABP数据过滤器仅实现在Entity Framework上。对于其它ORM工具则尚不可用。但是, 实际上,你可以模仿这个模式到其它使用仓储来取得数据的案例下。这此案例中, 你可以创建一个定制的仓储并且覆写GetAll方法,如果有需要的话,可以一起修改其它资料检索方法。


希望更多国内的架构师能关注到ABP这个项目,也许这其中有能帮助到您的地方,也许有您的参与,这个项目可以发展得更好。

欢迎加ABP架构设计交流QQ群:134710707

点这里进入ABP系列文章总目录

ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之18、ABP应用层——权限验证

    点这里进入ABP系列文章总目录 ABP(现代ASP.NET样板开发框架)系列之18.ABP应用层——权限验证 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目 ...

  2. ABP(现代ASP.NET样板开发框架)系列之20、ABP展现层——动态生成WebApi

    点这里进入ABP系列文章总目录 ABP(现代ASP.NET样板开发框架)系列之20.ABP展现层——动态生成WebApi ABP是“ASP.NET Boilerplate Project (ASP.N ...

  3. ABP(现代ASP.NET样板开发框架)系列之3、ABP分层架构

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之3.ABP分层架构 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  4. ABP(现代ASP.NET样板开发框架)系列之4、ABP模块系统

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之4.ABP模块系统 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  5. ABP(现代ASP.NET样板开发框架)系列之5、ABP启动配置

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之5.ABP启动配置 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  6. ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  7. ABP(现代ASP.NET样板开发框架)系列之7、ABP Session管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之7.ABP Session管理 ABP是“ASP.NET Boilerplate Project (ASP.NET ...

  8. ABP(现代ASP.NET样板开发框架)系列之8、ABP日志管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之8.ABP日志管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  9. ABP(现代ASP.NET样板开发框架)系列之9、ABP设置管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之9.ABP设置管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

随机推荐

  1. 微信企业号 获取AccessToken

    目录 1. AccessToken介绍 2. 示例代码 1. AccessToken介绍 1.1 什么是AccessToken AccessToken即访问凭证,业务服务器每次主动调用企业号接口时需要 ...

  2. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  3. 我是如何在SQLServer中处理每天四亿三千万记录的

    首先声明,我只是个程序员,不是专业的DBA,以下这篇文章是从一个问题的解决过程去写的,而不是一开始就给大家一个正确的结果,如果文中有不对的地方,请各位数据库大牛给予指正,以便我能够更好的处理此次业务. ...

  4. ExtJS 4.2 Date组件扩展:添加清除按钮

    ExtJS中除了提供丰富的组件外,我们还可以扩展他的组件. 在这里,我们将在Date日期组件上添加一个[清除]按钮,用于此组件已选中值的清除. 目录 1. Date组件介绍 2. 主要代码说明 3. ...

  5. 23种设计模式--工厂模式-Factory Pattern

    一.工厂模式的介绍       工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...

  6. CentOS 7配置LNMP开发环境及配置文件管理

    安装并配置MySQL 5.6 从CentOS从7.x开始默认使用MariaDB.MariaDB完全兼容MySQL,包括API和命令行.但是很多时候我们还是会想要安装MySQL,所以不能直接通过yum命 ...

  7. Javascript实用方法

    这篇我主要记录一些在工作中常用的.实用的方法. String trim 字符串方法中的trim主要用来去空格使用,很多时候,在后台做参数处理的时候,我们都会使用该方法,比如在获取用户输入的账户时 va ...

  8. [原]Cachedb 网络模块文档

    Cachedb 网络模块文档 整体结构 多路复用 (epoll 模块) 事件驱动 (事件封装) 缓冲管理 (上层buffer管理) 设计思想 层次化的设计,每一个模块只调用上一个模块的接口,并将耦合聚 ...

  9. 编写高质量代码:改善Java程序的151个建议(第8章:异常___建议114~117)

    建议114:不要在构造函数中抛出异常 Java异常的机制有三种: Error类及其子类表示的是错误,它是不需要程序员处理也不能处理的异常,比如VirtualMachineError虚拟机错误,Thre ...

  10. TCP服务和首部知识点小结

    服务 应用程序会被TCP分割成数据段,而UDP不分割. TCP有超时重传和确认 如果检验和出错将丢弃 IP数据包可能会失序或者重复,所以TCP会处理 滑动窗口来进行流量控制 对字节流的内容不做任何解释 ...