zoukankan      html  css  js  c++  java
  • 文章翻译:ABP如何在EF core中添加数据过滤器

    原文地址: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是什么。

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

  • 相关阅读:
    Java设计模式菜鸟系列(四)工厂方法模式建模与实现
    决策树分类
    SVD神秘值分解
    省市区三级联动菜单(附数据库)
    POJ 3076 Sudoku (dancing links)
    HDOJ 4862 Jump
    BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 站点设置
    Transparency Tutorial with C#
    【剑指offer】不用加减乘除做加法
    POJ2112_Optimal Milking(网洛流最大流Dinic+最短路Flody+二分)
  • 原文地址:https://www.cnblogs.com/bamboo-zhang/p/11765037.html
Copyright © 2011-2022 走看看