From 109ca428bb9237c5b863c9a2361b36d2ae490783 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 18 Jun 2024 16:19:24 +0800 Subject: [PATCH] Update `Data-Filtering.md`. --- docs/en/Data-Filtering.md | 83 +++++++++++++++++-- docs/zh-Hans/Data-Filtering.md | 83 +++++++++++++++++-- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 11 ++- .../AbpEfCoreDataFilterDbFunctionMethods.cs | 2 +- .../AbpEfCoreGlobalFilterOptions.cs | 4 + 5 files changed, 165 insertions(+), 18 deletions(-) diff --git a/docs/en/Data-Filtering.md b/docs/en/Data-Filtering.md index 830979e710..4706912fe7 100644 --- a/docs/en/Data-Filtering.md +++ b/docs/en/Data-Filtering.md @@ -217,17 +217,14 @@ protected override bool ShouldFilterEntity(IMutableEntityType entityTyp return base.ShouldFilterEntity(entityType); } -protected override Expression> CreateFilterExpression() +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) { - var expression = base.CreateFilterExpression(); + var expression = base.CreateFilterExpression(modelBuilder); if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) { - Expression> isActiveFilter = - e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); - expression = expression == null - ? isActiveFilter - : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); } return expression; @@ -251,6 +248,78 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } ```` +#### Using User-defined function mapping for global filters + +Using [User-defined function mapping](https://learn.microsoft.com/en-us/ef/core/querying/user-defined-function-mapping) for global filters will gain performance improvements. + +To use this feature, you need to change your DbContext like below: + +````csharp +protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled() ?? false; + +protected override bool ShouldFilterEntity(IMutableEntityType entityType) +{ + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + return true; + } + + return base.ShouldFilterEntity(entityType); +} + +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) +{ + var expression = base.CreateFilterExpression(modelBuilder); + + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + + if (UseDbFunction()) + { + isActiveFilter = e => IsActiveFilter(((IIsActive)e).IsActive, true); + + var abpEfCoreCurrentDbContext = this.GetService(); + modelBuilder.HasDbFunction(typeof(MyProjectNameDbContext).GetMethod(nameof(IsActiveFilter))!) + .HasTranslation(args => + { + // (bool isActive, bool boolParam) + var isActive = args[0]; + var boolParam = args[1]; + + if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled() == true) + { + // isActive == true + return new SqlBinaryExpression( + ExpressionType.Equal, + isActive, + new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping), + boolParam.Type, + boolParam.TypeMapping); + } + + // empty where sql + return new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping); + }); + } + + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + } + + return expression; +} + +public static bool IsActiveFilter(bool isActive, bool boolParam) +{ + throw new NotSupportedException(AbpEfCoreDataFilterDbFunctionMethods.NotSupportedExceptionMessage); +} + +public override string GetCompiledQueryCacheKey() +{ + return $"{base.GetCompiledQueryCacheKey()}:{IsActiveFilterEnabled}"; +} +```` + ### MongoDB ABP abstracts the `IMongoDbRepositoryFilterer` interface to implement data filtering for the [MongoDB Integration](MongoDB.md), it works only if you use the repositories properly. Otherwise, you should manually filter the data. diff --git a/docs/zh-Hans/Data-Filtering.md b/docs/zh-Hans/Data-Filtering.md index 8a43ad93b1..01c4590761 100644 --- a/docs/zh-Hans/Data-Filtering.md +++ b/docs/zh-Hans/Data-Filtering.md @@ -166,17 +166,14 @@ protected override bool ShouldFilterEntity(IMutableEntityType entityTyp return base.ShouldFilterEntity(entityType); } -protected override Expression> CreateFilterExpression() +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) { - var expression = base.CreateFilterExpression(); + var expression = base.CreateFilterExpression(modelBuilder); if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) { - Expression> isActiveFilter = - e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); - expression = expression == null - ? isActiveFilter - : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); } return expression; @@ -186,6 +183,78 @@ protected override Expression> CreateFilterExpression DataFilter?.IsEnabled() ?? false; + +protected override bool ShouldFilterEntity(IMutableEntityType entityType) +{ + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + return true; + } + + return base.ShouldFilterEntity(entityType); +} + +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) +{ + var expression = base.CreateFilterExpression(modelBuilder); + + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + + if (UseDbFunction()) + { + isActiveFilter = e => IsActiveFilter(((IIsActive)e).IsActive, true); + + var abpEfCoreCurrentDbContext = this.GetService(); + modelBuilder.HasDbFunction(typeof(MyProjectNameDbContext).GetMethod(nameof(IsActiveFilter))!) + .HasTranslation(args => + { + // (bool isActive, bool boolParam) + var isActive = args[0]; + var boolParam = args[1]; + + if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled() == true) + { + // isActive == true + return new SqlBinaryExpression( + ExpressionType.Equal, + isActive, + new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping), + boolParam.Type, + boolParam.TypeMapping); + } + + // empty where sql + return new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping); + }); + } + + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + } + + return expression; +} + +public static bool IsActiveFilter(bool isActive, bool boolParam) +{ + throw new NotSupportedException(AbpEfCoreDataFilterDbFunctionMethods.NotSupportedExceptionMessage); +} + +public override string GetCompiledQueryCacheKey() +{ + return $"{base.GetCompiledQueryCacheKey()}:{IsActiveFilterEnabled}"; +} +```` + ### MongoDB ABP抽象了 `IMongoDbRepositoryFilterer` 接口为[MongoDB 集成](MongoDB.md)实现数据过滤, 只有正确的使用仓储,它才会工作. 否则你需要手动过滤数据. diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 6f45b75dc3..794058ad6e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -798,7 +798,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, "IsDeleted"); - if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction && DbContextOptions.FindExtension() != null) + if (UseDbFunction()) { expression = e => AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilter(((ISoftDelete)e).IsDeleted, true); modelBuilder.ConfigureSoftDeleteDbFunction(AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, this.GetService()); @@ -809,7 +809,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, "TenantId") == CurrentTenantId; - if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction && DbContextOptions.FindExtension() != null) + if (UseDbFunction()) { multiTenantFilter = e => AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilter(((IMultiTenant)e).TenantId, CurrentTenantId, true); modelBuilder.ConfigureMultiTenantDbFunction(AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilterMethodInfo, this.GetService()); @@ -821,8 +821,13 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return expression; } + protected virtual bool UseDbFunction() + { + return LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction && DbContextOptions.FindExtension() != null; + } + public virtual string GetCompiledQueryCacheKey() { - return $"{CurrentTenantId?.ToString() ?? "Null"}{IsSoftDeleteFilterEnabled}:{IsMultiTenantFilterEnabled}"; + return $"{CurrentTenantId?.ToString() ?? "Null"}:{IsSoftDeleteFilterEnabled}:{IsMultiTenantFilterEnabled}"; } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs index 35694433ed..10cb6be280 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; public static class AbpEfCoreDataFilterDbFunctionMethods { - private const string NotSupportedExceptionMessage = "Your EF Core database provider does not support 'User-defined function mapping'." + + public const string NotSupportedExceptionMessage = "Your EF Core database provider does not support 'User-defined function mapping'." + "Please set 'UseDbFunction' of 'AbpEfCoreGlobalFilterOptions' to false to disable it." + "See https://learn.microsoft.com/en-us/ef/core/querying/user-defined-function-mapping for more information." ; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs index e1641cd2a1..f97031ff13 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs @@ -2,5 +2,9 @@ namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; public class AbpEfCoreGlobalFilterOptions { + /// + /// Use User-defined function mapping to filter data. + /// https://learn.microsoft.com/en-us/ef/core/querying/user-defined-function-mapping + /// public bool UseDbFunction { get; set; } }