返回总目录


本篇目录

介绍###

软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除的"。这样,如果一个实体是软删除的,那么它不应该在应用中检索到。为了实现这个目的,我们应该在每一个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. 3小时搞定一个简单的MIS系统案例Northwind,有视频、有源代码下载、有真相

    一.瞎扯框架.架构 楼主自从1998年从C语言.MASM.Foxbase开始学计算机开始接触这个行当16年以来,2001年干第一份与程序.软件.然后是各种屌的东西开始,差不多干了13年了,这13年来, ...

  2. 用goto做异常处理

    http://www.cnblogs.com/trying/archive/2012/06/25/2863753.html 今天在CSDN上看到的关于错误返回值的讨论,感觉非常有趣. 从中可以看出被教 ...

  3. 序言<EntityFramework6.0>

    Entity Framework是微软战略性的数据访问技术,不同与早期访问技术,Entity Framework并不耦合在Visual Studio中,它提供了一个全面的, 基于模型的生态系统,使您能 ...

  4. [译]:Xamarin.Android开发入门——Hello,Android快速上手

    返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...

  5. 关于 js 一些基本的东西

    r.js 可以打包(可以实现前端文件的压缩与合并). 客户端尽量遵循 amd 规范. 推荐使用 requirejs 规范. requirejs 简单教程: http://www.runoob.com/ ...

  6. tmux 简单命令

    tmux 大概结构图: 如果你已经安装了tmux,则输入tmux会进入tmux功能界面 0. tmux ls 列出已经存在session 1. tmux new -s foo  新建session   ...

  7. 【vuejs小项目——vuejs2.0版本】单页面搭建

    http://router.vuejs.org/zh-cn/essentials/nested-routes.html 使用嵌套路由开发,这里会出错主要把Vue.use(VueRouter);要进行引 ...

  8. acm入门 杭电1001题 有关溢出的考虑

    最近在尝试做acm试题,刚刚是1001题就把我困住了,这是题目: Problem Description In this problem, your task is to calculate SUM( ...

  9. [ACM训练] 算法初级 之 搜索算法 之 广度优先算法BFS (POJ 3278+1426+3126+3087+3414)

    BFS算法与树的层次遍历很像,具有明显的层次性,一般都是使用队列来实现的!!! 常用步骤: 1.设置访问标记int visited[N],要覆盖所有的可能访问数据个数,这里设置成int而不是bool, ...

  10. 基于synchronized 或 ReadWriteLock实现 简单缓存机制

    package cn.xxx.xxx; import java.util.HashMap; import java.util.Map; import java.util.concurrent.lock ...