在EF EntityFramework Core 5.0中实现全局查询过滤器 【多租户】和【软删除】

如果全局查询筛选器对你来说是一个新事物,请阅读此地址:Https://docs.microsoft.com/en-us/ef/core/querying/filters

在.NET 5 应用程序中,有多种实现过滤的方法,为什么我们没有选择如下的方法:

未使用的方案:

1.OnModelCreating方法中的过滤

当然可以,但是在DB First项目中,这种方法非常耗时,因为每次做脚手架时都必须修改它,所以我们说了PASS。

2.Azure SQL DB RLS(行级安全性)

这个方法也有效,在DB First方法中是非常推荐的,但是我们有一个场景,用户和租户之间的基数是N:n,这可能会引起一些问题。如果您没有N:n基数,请查看它,它可能是您所需要的(在这里可以找到更多关于这一点的信息:Https://docs.microsoft.com/en-us/sql/relational-databases/security/row-level-security?view=sql-server-ver15 )

3.对每个控制器进行过滤

你可能会考虑这一点,如果你有一个非常小的应用程序,只有几个控制器,但它可能会到来的时候,你会后悔这个决定?

最终的解决方案如下:

因此,最终使用了全局查询过滤器(GlobalQuery Filters)

下面的代码段检查是否存在具有TenantId和IsDelated字段的模型,并根据这些字段筛选对象。

EFCore 官方说明不支持多个过滤器,因此我们必须使用比较复杂的表达式和表达式合并方法来解决此问题

下面是具体的代码示例:

public partial class MasterContext : DbContext
{
    public Guid TenantId { get; set; }
    public List<Guid> _tenantIds;
    public bool isAllActive; // 这适用于租户需要所有数据、软删除行和活动行的场景
    public int hashCode;

    public void OnCreating(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
    {
        // 这是一个全局过滤器过滤器,用于检查是否具有IsDeleted和TenantIds列
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            var isDeletedProperty = entityType.FindProperty("IsDeleted");
            var tenantIdProperty = entityType.FindProperty("TenantId");

            // 如果我们同时具有IsDeleted和TenantId列(不限制IsDeleted)
            if (((isDeletedProperty != null) && (tenantIdProperty != null))
                    && isDeletedProperty.ClrType == typeof(bool) && !isAllActive)
            {
                var parameter = Expression.Parameter(entityType.ClrType, "p");

                
                var filterIsDeleted = Expression.Equal(Expression.Property(parameter, isDeletedProperty.PropertyInfo)
                                                      , Expression.Constant(false, typeof(bool)));

                // 检查是否有租户信息
                if (_tenantIds != null)
                {
                    
                    var tenantid = _tenantIds.First();
                    var filterTenantId = Expression.Equal(Expression.Property(parameter, tenantIdProperty.PropertyInfo)
                                                , Expression.Constant(tenantid, typeof(Guid)));

              
                    List<Guid> localtenantIds = new List<Guid>(_tenantIds);
                    localtenantIds.Remove(tenantid);

             
                    foreach (var tenantidloop in localtenantIds)
                    {
                        filterTenantId = Expression.Or(filterTenantId, Expression.Equal(Expression.Property(parameter, tenantIdProperty.PropertyInfo)
                                               , Expression.Constant(tenantidloop, typeof(Guid))));

                    }

                    //合并过滤器
                    MutableEntityTypeExtensions.SetQueryFilter(entityType, Expression.Lambda(Expression.And(filterIsDeleted, filterTenantId), parameter));
                }
                //虚拟过滤,因此我们排除了所有其他租户
                else
                {
                    var tenantid = new Guid("00000000-0000-0000-0000-000000000000");
                    var filterTenantId = Expression.Equal(Expression.Property(parameter, tenantIdProperty.PropertyInfo)
                                                , Expression.Constant(tenantid, typeof(Guid)));

                    //合并过滤器
                    MutableEntityTypeExtensions.SetQueryFilter(entityType, Expression.Lambda(Expression.And(filterIsDeleted, filterTenantId), parameter));
                }
            }
            else
            {
                // 这是一个全局过滤器,用于检查对象是否具有TenantId列
                if (((isDeletedProperty != null) && (tenantIdProperty == null)) 
                 && isDeletedProperty.ClrType == typeof(bool))
                {
                    var parameter = Expression.Parameter(entityType.ClrType, "p");

                    //is deleted only
                    var filterIsDeleted = Expression.Equal(Expression.Property(parameter, isDeletedProperty.PropertyInfo)
                                                          , Expression.Constant(false, typeof(bool)));

                    MutableEntityTypeExtensions.SetQueryFilter(entityType, Expression.Lambda(filterIsDeleted, parameter));
                }
                else
                {
                    // 这是一个全局过滤器,用于检查对象是否具有TenantId列 
                    if ((((isDeletedProperty == null) && (tenantIdProperty != null)) /
                     && isDeletedProperty.ClrType == typeof(bool)) ||
                     (((tenantIdProperty != null)) && isAllActive))  

                    {
                        var parameter = Expression.Parameter(entityType.ClrType, "p");
                        
                        if (_tenantIds.Any())
                        {
                            
                            var tenantid = _tenantIds.First();
                            var filterTenantId = Expression.Equal(Expression.Property(parameter, tenantIdProperty.PropertyInfo)
                                                        , Expression.Constant(tenantid, typeof(Guid)));
                            _tenantIds.Remove(tenantid);

                            
                            foreach (var tenantidloop in _tenantIds)
                            {
                                filterTenantId = Expression.Or(filterTenantId, Expression.Equal(Expression.Property(parameter, tenantIdProperty.PropertyInfo)
                                                       , Expression.Constant(tenantidloop, typeof(Guid))));

                            }

                            //合并过滤器
                            MutableEntityTypeExtensions.SetQueryFilter(entityType, Expression.Lambda(filterTenantId, parameter));
                        }
                    }
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/dowork/p/14711140.html