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

在本文中,我将解释如何在EF core中添加自定义数据过滤器。

我们将为OrganizationUnit 创建一个过滤器,并从IMayHaveOrganizationUnit接口继承的实体,根据登录用户的组织单元自动过滤。

我们将使用asp.net core和asp.net查询模板。您可以在https://aspnetboilerplate.com/templates上创建一个项目,并应用以下步骤来查看自定义组织单元筛选器的运行情况。

创建和更新实体

创建实体

创建名为Document的实体继承自IMayHaveOrganizationUnit接口(IMayHaveOrganizationUnit接口在abp框架中定义)。

public class Document : Entity, IMayHaveOrganizationUnit
{
public string Title { get; set; } public string Content { get; set; } public long? OrganizationUnitId { get; set; }
}

  在你的DbContext中添加Document实体

更新User类

向user.cs添加OrganizationUnitId。我们将使用用户的OrganizationUnitId 字段来过滤Document实体。

public class User : AbpUser<User>
{
public const string DefaultPassword = "123qwe"; public static string CreateRandomPassword()
{
return Guid.NewGuid().ToString("N").Truncate(16);
} public int? OrganizationUnitId { get; set; } public static User CreateTenantAdminUser(int tenantId, string emailAddress)
{
var user = new User
{
TenantId = tenantId,
UserName = AdminUserName,
Name = AdminUserName,
Surname = AdminUserName,
EmailAddress = emailAddress
}; user.SetNormalizedNames(); return user;
}
}

  

添加迁移

使用add migration命令添加迁移并运行update database以将更改应用于数据库。

创建Claim

我们需要在Claim中存储登录用户的OrganizationUnitId ,这样就可以得到它,以便在DbContext中过滤IMayHaveOrganizationUnit 实体。为此,重写UserClaimsPrincipalFactory类的CreateAsync方法,并将登录用户的 OrganizationUnitId 添加到如下声明中。

public class UserClaimsPrincipalFactory : AbpUserClaimsPrincipalFactory<User, Role>
{
public UserClaimsPrincipalFactory(
UserManager userManager,
RoleManager roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(
userManager,
roleManager,
optionsAccessor)
{
} public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim("Application_OrganizationUnitId", user.OrganizationUnitId.HasValue ? user.OrganizationUnitId.Value.ToString() : "")); return claim;
}
}

  

注册过滤器

在筛选DbContext中的实体之前,我们将注册过滤器,以便在代码中的某些情况下禁用它。

在YourProjectNameEntityFrameworkModule 的PreInitialize 方法中注册筛选器,以从当前工作单元管理器获取它。

public override void PreInitialize()
{
... //register filter with default value
Configuration.UnitOfWork.RegisterFilter("MayHaveOrganizationUnit", true);
}

  

配置DbContext

我们需要使用OrganizationUnitId 的值来过滤DbContext中的IMayHaveOrganizationUnit 实体。

为此,首先在DbContext中添加如下字段:

protected virtual int? CurrentOUId => GetCurrentUsersOuIdOrNull();

在DbContext中定义如下GetCurrentUsersOuIdOrNull方法,并使用propert注入将IPrincipalAccessor 注入到DbContext中;

public class CustomFilterSampleDbContext : AbpZeroDbContext<Tenant, Role, User, CustomFilterSampleDbContext>
{
public DbSet<Document> Documents { get; set; } public IPrincipalAccessor PrincipalAccessor { get; set; } protected virtual int? CurrentOUId => GetCurrentUsersOuIdOrNull(); public CustomFilterSampleDbContext(DbContextOptions<CustomFilterSampleDbContext> options)
: base(options)
{ } protected virtual int? GetCurrentUsersOuIdOrNull()
{
var userOuClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_OrganizationUnitId");
if (string.IsNullOrEmpty(userOuClaim?.Value))
{
return null;
} return Convert.ToInt32(userOuClaim.Value);
}
}

  之后,让我们向DbContext添加一个属性,以获取MayHaveOrganizationUnit 过滤器是否已启用。

protected virtual bool IsOUFilterEnabled => CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled("MayHaveOrganizationUnit") == true;

  

AbpDbContext 定义了两个与数据筛选器相关的方法。一个是ShouldFilterEntity ,另一个是CreateFilterExpression。ShouldFilterEntity方法决定是否过滤实体。CreateFilterExpression方法为要筛选的实体创建筛选表达式。

为了过滤从IMayHaveOrganizationUnit继承的实体,我们需要重写这两个方法。

首先,重写如下所示的ShouldFilterEntity 方法;

protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
{
if (typeof(IMayHaveOrganizationUnit).IsAssignableFrom(typeof(TEntity)))
{
return true;
} return base.ShouldFilterEntity<TEntity>(entityType);
}

然后,重写CreateFilterExpression方法,如下所示;

protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{
var expression = base.CreateFilterExpression<TEntity>(); if (typeof(IMayHaveOrganizationUnit).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> mayHaveOUFilter = e => ((IMayHaveOrganizationUnit)e).OrganizationUnitId == CurrentOUId || (((IMayHaveOrganizationUnit)e).OrganizationUnitId == CurrentOUId) == IsOUFilterEnabled;
expression = expression == null ? mayHaveOUFilter : CombineExpressions(expression, mayHaveOUFilter);
} return expression;
}

  以下是DbContext的最终版本:

public class CustomFilterSampleDbContext : AbpZeroDbContext<Tenant, Role, User, CustomFilterSampleDbContext>
{
public DbSet<Document> Documents { get; set; } public IPrincipalAccessor PrincipalAccessor { get; set; } protected virtual int? CurrentOUId => GetCurrentUsersOuIdOrNull(); protected virtual bool IsOUFilterEnabled => CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled("MayHaveOrganizationUnit") == true; public CustomFilterSampleDbContext(DbContextOptions<CustomFilterSampleDbContext> options)
: base(options)
{ } protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
{
if (typeof(IMayHaveOrganizationUnit).IsAssignableFrom(typeof(TEntity)))
{
return true;
}
return base.ShouldFilterEntity<TEntity>(entityType);
} protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{
var expression = base.CreateFilterExpression<TEntity>();
if (typeof(IMayHaveOrganizationUnit).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> mayHaveOUFilter = e => ((IMayHaveOrganizationUnit)e).OrganizationUnitId == CurrentOUId || (((IMayHaveOrganizationUnit)e).OrganizationUnitId == CurrentOUId) == IsOUFilterEnabled;
expression = expression == null ? mayHaveOUFilter : CombineExpressions(expression, mayHaveOUFilter);
} return expression;
} protected virtual int? GetCurrentUsersOuIdOrNull()
{
var userOuClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_OrganizationUnitId");
if (string.IsNullOrEmpty(userOuClaim?.Value))
{
return null;
} return Convert.ToInt32(userOuClaim.Value);
}
}

  

测试过滤器

要测试MayHaveOrganizationUnit筛选器,请创建一个组织单元,并将其用户ID设置为2(默认租户的管理用户的ID)和TenantID设置为1(默认租户的ID)。然后,在数据库中创建文档记录。用组织机构的OrganizationUnitId 设置默认租户管理员和已创建的文档。

在HomeController中从数据库获取数据:

[AbpMvcAuthorize]
public class HomeController : CustomFilterSampleControllerBase
{
private readonly IRepository<Document> _documentRepository; public HomeController(IRepository<Document> documentRepository)
{
_documentRepository = documentRepository;
} public ActionResult Index()
{
var documents = _documentRepository.GetAllList();
var documentTitles = string.Join(",", documents.Select(e => e.Title).ToArray()); return Content(documentTitles);
}
}

当您以host 用户身份登录时,应该会看到一个emtpy页面。但是,如果您以默认租户的管理员用户身份登录,您将看到以下文档标题:(以下丢失一张图片,请自行脑补,O(∩_∩)O哈哈~)

禁用筛选器

可以禁用如下筛选器:

[AbpMvcAuthorize]
public class HomeController : CustomFilterSampleControllerBase
{
private readonly IRepository<Document> _documentRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager; public HomeController(IRepository<Document> documentRepository, IUnitOfWorkManager unitOfWorkManager)
{
_documentRepository = documentRepository;
_unitOfWorkManager = unitOfWorkManager;
} public ActionResult Index()
{
using (_unitOfWorkManager.Current.DisableFilter("MayHaveOrganizationUnit"))
{
var documents = _documentRepository.GetAllList();
var documentTitles = string.Join(",", documents.Select(e => e.Title).ToArray()); return Content(documentTitles);
}
}
}

  在这种情况下,将从数据库检索所有文档记录,而不管登录用户OrganizationUnitId是什么。

翻译完成,但并不是我想要的功能 ┭┮﹏┭┮

文章翻译:ABP如何在EF core中添加数据过滤器的更多相关文章

  1. 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...

  2. 9.4 翻译系列:EF 6以及 EF Core中的NotMapped特性(EF 6 Code-First系列)

    原文链接:http://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-f ...

  3. 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...

  4. 11.翻译系列:在EF 6中配置一对零或者一对一的关系【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-fi ...

  5. EF Core中避免贫血模型的三种行之有效的方法(翻译)

    Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...

  6. 如何在EF Core 使用存储过程

    使用EF Core框架能快速的帮助我们进行常规的数据处理和项目开发,但是ORM虽然好用,但是在许多复杂逻辑的数据处理时,我个人还是偏向用SQL和存储过程的方式去处理,但是研究了一下目前最新版本的EF ...

  7. 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

    项目开发中的一些注意事项以及技巧总结   1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...

  8. EF Core中的多对多映射如何实现?

    EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的.但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考<你必须掌握的Entity ...

  9. EF Core中执行Sql语句查询操作之FromSql,ExecuteSqlCommand,SqlQuery

    一.目前EF Core的版本为V2.1 相比较EF Core v1.0 目前已经增加了不少功能. EF Core除了常用的增删改模型操作,Sql语句在不少项目中是不能避免的. 在EF Core中上下文 ...

随机推荐

  1. oracle数据段详解

    Tablespace(表空间):表空间是数据库的逻辑划分,一个表空间只能属于一个数据库.所有的数据库对象都放在指定的表空间中,但主要存放的对象是表,所以称为表空间. 默认的系统表空间:system.s ...

  2. C++学习 之 函数的重载及内联(笔记)

    1.函数的使用: 1.1 将数组传递给函数: 当需要给函数传递数组作为参数时,其实传过来的是实参的地址就相当于使用引用或指针作为形参. 例: int DisPlayArray(int Number[] ...

  3. GitHub从小白到熟悉<二>

    创建 仓库  

  4. vue-router和webpack懒加载,页面性能优化篇

    在vue单页应用中,当项目不断完善丰富时,即使使用webpack打包,文件依然是非常大的,影响页面的加载.如果我们能把不同路由对应的组件分割成不同的代码块,当路由被访问时才加载对应的组件(也就是按需加 ...

  5. 牛客 545C 出题人的数组 (贪心)

    出题人有两个数组A,B,请你把两个数组归并起来使得$cost=\sum i c_i$最小. 归并要求原数组的数的顺序在新数组中不改变. 贪心水题 对于一段序列$A_i,A_{i+1},...,A_r$ ...

  6. git解决pre-commit hook failed的问题

    最近在提交前端代码的时候发现提交不上去,一直报错 一.错误详情 二.错误分析 1.刚开始用vsCode提交,后更改为命令提交,依旧报错: 2.经过查询资料,发现是pre-commit钩子的原因.   ...

  7. Java内存泄漏真实案例

    内存泄漏:当不再需要一个对象时,垃圾收集器会回收它:如果不需要的对象一直在产生而不被收回,就称作“内存泄漏”. 以下为本人在工作中遇到的内存泄漏的案例: 1.对于大量的请求,使用了Executors. ...

  8. jenkins自动部署代码到多台服务器

    下面讲一下如何通过一台jenkins服务器构建后将代码分发到其他的远程服务器,即jenkins自动部署代码到多台服务器. 1.下载 pulish over ssh 插件 2.系统管理 -> 系统 ...

  9. Python测试开发必知必会-PEP

    互联网发展了许多年,不仅颠覆了很多行业,还让很多职位有了更多的用武之地.产品发布迭代速度不断加快,让测试开发这个岗位简直火得不要不要的. Python语言,作为一种更接近人来自然语言的开发语言,以简洁 ...

  10. VMware的linux虚拟机配置ip后无法ping通宿主机

    VMware的linux虚拟机配置ip(使用eth0)后无法ping通宿主机,同样宿主机无法ping通linux虚拟机. 可能原因:linux虚拟机使用的网卡,与本机使用的网卡不同,配置成与本机一致的 ...