返回总目录


本篇目录

介绍###

软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除的"。这样,如果一个实体是软删除的,那么它不应该在应用中检索到。为了实现这个目的,我们应该在每一个select实体查询操作中添加一个SQL where条件,如“IsDeleted=false”。这是乏味但是很重要的一项容易忘记的任务。因此,这项工作应该自动完成。

ABP提供了数据过滤器,它们可以基于某些规则自动过滤查询。有很多预定义的过滤器,但你也可以创建自己的过滤器。

预定义过滤器###

ISoftDelete

软删除过滤器用于当查询数据库时自动过滤(从结果中提取)已经删除的实体。如果实体应该是软删除的,那么它必须实现只定义了IsDelete属性的 ISoftDelete接口,例如:

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

实际上,Person实体并没有从数据库中删除,只是当要删除它时将它的 IsDelete属性设置成了true。当使用 IRepository.Delete方法时,ABP会自动处理(你可以手动设置IsDelete为true,但是Delete方法更自然且更受人欢迎)。

实现了ISoftDelete之后,当从数据库获取Person的列表时,已经软删除的person是不会检索到的。这里有一个使用了person仓储获得所有person的例子:

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),那么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呢?

IMustHaveTenant默认是开启的。

如果当前的用户没有登录到系统或者当前的用户是一个租主用户(租主用户是可以管理租户和租户数据的更高级用户),ABP会自动关闭IMustHaveTenant过滤器。因此,所有租户的所有数据都可以被检索到。注意这是没有涉及到安全的情况,你应该总是要对敏感的数据进行授权。

IMayHaveTenant

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

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

如果TenantId的值是null,就意味着这是一个 租主实体;如果值不为null,就意味着该实体被一个 租户拥有,该租户的Id就是该TenantId。ABP使用了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包含了ABP标准软删除过滤器的名称的常量字符串。

people2可以获得已经删除的person实体,然而people1和people3只会获得没有删除的实体。使用 using语句,你可以在一个 作用域(scope)中关闭过滤器。如果你没有使用using语句,那么过滤器在当前工作单元结束前都是关闭的,除非你显式再次开启它。

你可以像上面的例子那样注入IUnitOfWorkManager使用。此外,你也可以在应用服务(它派生自ApplicationService类)中使用CurrentUnitOfWork属性作为快捷方式。

关于using语句

如果过滤器是开启的,当你在using语句中调用DisableFilter方法时,那么过滤器会关闭,然后,当using语句结束时,它会自动再次开启。但是如果在使用using语句之前过滤器已经关闭了,那么DisableFilter实际上什么都不会做,而且在using语句结束后仍然是关闭的。

开启过滤器###

你可以在工作单元中使用EnableFilter方法来开启一个过滤器,和DisableFilter很相似。EnableFilter也返回disable来自动再次关闭该过滤器。

设置过滤器参数###

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

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

另一个例子:为IMayHaveTenant过滤器设置tenantId值:

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

SetFilterParameter方法也返回一个IDisposable。因此,我们可以在一个using语句中使用它,在using语句结束时自动还原旧值

定义自定义过滤器###

要创建一个自定义过滤器并集成到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,因此我们可以使用它的规则来定义该过滤器。在我们的 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"))
{
CurrentUnitOfWork.SetFilterParameter("PersonFilter", "personId", 42);
var phones = _phoneRepository.GetAllList();
//...
}

我们可以从一些源中获得personId而不是静态代码中。上面的例子是对于参数化的过滤器来说的。过滤器可以有零个或更多的参数。如果它没有参数,就不需要设置过滤器的值了。此外,如果它默认是开启的,它就不需要手动开启了(当然,我们还可以关闭它)。

EntityFramework.DynamicFilters文档

关于动态数据过滤器的更多信息,请看github上的文档

我们也可以为安全,激活/未激活的实体等创建自定义的过滤器。

其他ORM###

ABP已经实现了EF和NH的数据过滤。对于其他的ORM还不可用。但是实际上,只要你使用仓储获得数据,你就可以为绝大多数情况模拟数据过滤。对于这种情况,如果需要的话,你可以创建自定义的仓储,然后重写 GetAll和其他的数据检索方法。

ABP理论学习之数据过滤器的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  2. ABP中的数据过滤器

      本文首先介绍了ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),然后介绍了如何实现一个自定义过滤器,最后介绍了在软件开发过程中遇到的实际问题,同时给出了 ...

  3. ABP 配置全局数据过滤器

    ABP官方数据过滤的地址:https://aspnetboilerplate.com/Pages/Documents/Data-Filters 中文可以看这个:https://aspnetboiler ...

  4. ABP 配置全局数据过滤器 II

    第一篇 那种写法有些复杂, 简单办法是直接注入 切换到 ***.EntityFramework 项目 在Uow 里面创建 ***EfUnitOfWork.cs 类 public class Coope ...

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

    数据过滤器 介绍 预定义过滤器 ISoftDelete 何时使用? IMustHaveTenant 何时使用? IMayHaveTenant 何时使用 禁用过滤器 关于using语句 关于多租户 全局 ...

  6. ABP的数据过滤器(Data Filters)

    http://www.aspnetboilerplate.com/Pages/Documents/Data-Filters 我们在数据库开发中,一般会运用软删除 (soft delete)模式 ,即不 ...

  7. 文章翻译:ABP如何在EF core中添加数据过滤器

    原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...

  8. ABP理论学习之开篇介绍

    返回总目录 为了和2016年春节赛跑,完成该系列博客,我牺牲了今天中午的时间来完成该系列的第一篇----开篇介绍.开篇介绍嘛,读过大学教材的同学都知道,这玩意总是那么无聊,跟考试没关系,干脆直接跳过, ...

  9. ABP理论学习之多租户

    返回总目录 本篇目录 什么是多租户 ABP中的多租户 什么是多租户 维基百科:"软件多租户是指一种软件架构,在这种软件架构中,软件的一个实例运行在服务器上并且为多个租户服务".一个 ...

随机推荐

  1. 【Java EE 学习 78 中】【数据采集系统第十天】【Spring远程调用】

    一.远程调用概述 1.远程调用的定义 在一个程序中就像调用本地中的方法一样调用另外一个远程程序中的方法,但是整个过程对本地完全透明,这就是远程调用.spring已经能够非常成熟的完成该项功能了. 2. ...

  2. 公众号第三方平台开发 获取 component_verify_ticket 2015-07-05 10:16 59人阅读 评论(0) 收藏

    8.推送component_verify_ticket协议 在公众号第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL"每隔10分钟定时推送component_veri ...

  3. ECMAScript 5

    2009年12月,ECMAScript 5.02011年6月,ECMAscript 5.1版发布2015年6月,ECMAScript 6正式通过,成为国际标准ES6第一个版本 ES2015,发布于20 ...

  4. Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程)

    Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程) 声明:本教程在参考了以下博文,并经过自己的摸索后实际操作得出,本教程系本人原创,由于升级 ...

  5. shr 右移测试

    fdword :DWORD; procedure TForm10.btn1Click(Sender: TObject); var temp:DWORD; begin fdword :=; //7866 ...

  6. HashMap的内部实现机制,Hash是怎样实现的,什么时候ReHash

    1.HashMap的内部实现机制 HashMap是对数据结构中哈希表(Hash Table)的实现,Hash表又叫散列表.Hash表是根据关键码Key来访问其对应的值Value的数据结构,它通过一个映 ...

  7. HTML5之文件API

    问题很简单,做个上传文件的页面. <!-- multiple代表可上传多个文件 --> <input type="file" id="file" ...

  8. 统计学习方法 --- 感知机模型原理及c++实现

    参考博客 Liam Q博客 和李航的<统计学习方法> 感知机学习旨在求出将训练数据集进行线性划分的分类超平面,为此,导入了基于误分类的损失函数,然后利用梯度下降法对损失函数进行极小化,从而 ...

  9. Print a Binary Tree in Vertical Order

    http://www.geeksforgeeks.org/print-binary-tree-vertical-order/ package algorithms; import java.util. ...

  10. LintCode 78:Longest Common Prefix

      public class Solution { /** * @param strs: A list of strings * @return: The longest common prefix ...