From 24a1783c9f58ffc87d26d20a80ddd59cb1ad2209 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 13 May 2024 17:50:25 +0800 Subject: [PATCH 01/14] Get filter property name from `ModelBuilder`. In this way, we can customize the filter property name. --- docs/en/Data-Filtering.md | 4 ++-- docs/zh-Hans/Data-Filtering.md | 4 ++-- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 10 ++++++---- .../Abp/EntityFrameworkCore/TestMigrationsDbContext.cs | 2 ++ .../TestApp/EntityFrameworkCore/TestAppDbContext.cs | 2 ++ 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/en/Data-Filtering.md b/docs/en/Data-Filtering.md index 830979e710..2618ff183b 100644 --- a/docs/en/Data-Filtering.md +++ b/docs/en/Data-Filtering.md @@ -217,9 +217,9 @@ 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))) { diff --git a/docs/zh-Hans/Data-Filtering.md b/docs/zh-Hans/Data-Filtering.md index 8a43ad93b1..16793dd908 100644 --- a/docs/zh-Hans/Data-Filtering.md +++ b/docs/zh-Hans/Data-Filtering.md @@ -166,9 +166,9 @@ 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))) { 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 638860e742..e0650ee0f5 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -696,7 +696,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { if (mutableEntityType.BaseType == null && ShouldFilterEntity(mutableEntityType)) { - var filterExpression = CreateFilterExpression(); + var filterExpression = CreateFilterExpression(modelBuilder); if (filterExpression != null) { modelBuilder.Entity().HasAbpQueryFilter(filterExpression); @@ -765,19 +765,21 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return false; } - protected virtual Expression>? CreateFilterExpression() + protected virtual Expression>? CreateFilterExpression(ModelBuilder modelBuilder) where TEntity : class { Expression>? expression = null; if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { - expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, "IsDeleted"); + var softDeleteColumnName = modelBuilder.Entity().Metadata.FindProperty(nameof(ISoftDelete.IsDeleted))?.GetColumnName() ?? "IsDeleted"; + expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, softDeleteColumnName); } if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) { - Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, "TenantId") == CurrentTenantId; + var multiTenantColumnName = modelBuilder.Entity().Metadata.FindProperty(nameof(IMultiTenant.TenantId))?.GetColumnName() ?? "TenantId"; + Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, multiTenantColumnName) == CurrentTenantId; expression = expression == null ? multiTenantFilter : QueryFilterExpressionHelper.CombineExpressions(expression, multiTenantFilter); } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index 225e767d26..c341380338 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -49,6 +49,8 @@ public class TestMigrationsDbContext : AbpDbContext { b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate().HasDefaultValue(DateTime.Now); b.Property(x => x.HasDefaultValue).HasDefaultValue(DateTime.Now); + b.Property(x => x.TenantId).HasColumnName("Tenant_Id"); + b.Property(x => x.IsDeleted).HasColumnName("Is_Deleted"); }); modelBuilder.Entity(b => diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index e668a21e26..f6cd9eb4da 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -65,6 +65,8 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, { b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate().HasDefaultValue(DateTime.Now); b.Property(x => x.HasDefaultValue).HasDefaultValue(DateTime.Now); + b.Property(x => x.TenantId).HasColumnName("Tenant_Id"); + b.Property(x => x.IsDeleted).HasColumnName("Is_Deleted"); }); modelBuilder From 0f6cd42f583e6f915add55f9a555c3d9a02a2e4e Mon Sep 17 00:00:00 2001 From: Mohamed Seada Date: Sat, 15 Jun 2024 01:25:52 +0300 Subject: [PATCH 02/14] use post condition not null attribute --- framework/src/Volo.Abp.Core/Volo/Abp/Check.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs index 0e6820c913..d50a19401e 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs @@ -10,7 +10,7 @@ public static class Check { [ContractAnnotation("value:null => halt")] public static T NotNull( - T? value, + [System.Diagnostics.CodeAnalysis.NotNull] T? value, [InvokerParameterName][NotNull] string parameterName) { if (value == null) @@ -23,7 +23,7 @@ public static class Check [ContractAnnotation("value:null => halt")] public static T NotNull( - T? value, + [System.Diagnostics.CodeAnalysis.NotNull] T? value, [InvokerParameterName][NotNull] string parameterName, string message) { @@ -37,7 +37,7 @@ public static class Check [ContractAnnotation("value:null => halt")] public static string NotNull( - string? value, + [System.Diagnostics.CodeAnalysis.NotNull] string? value, [InvokerParameterName][NotNull] string parameterName, int maxLength = int.MaxValue, int minLength = 0) @@ -62,7 +62,7 @@ public static class Check [ContractAnnotation("value:null => halt")] public static string NotNullOrWhiteSpace( - string? value, + [System.Diagnostics.CodeAnalysis.NotNull] string? value, [InvokerParameterName][NotNull] string parameterName, int maxLength = int.MaxValue, int minLength = 0) @@ -87,7 +87,7 @@ public static class Check [ContractAnnotation("value:null => halt")] public static string NotNullOrEmpty( - string? value, + [System.Diagnostics.CodeAnalysis.NotNull] string? value, [InvokerParameterName][NotNull] string parameterName, int maxLength = int.MaxValue, int minLength = 0) @@ -111,7 +111,9 @@ public static class Check } [ContractAnnotation("value:null => halt")] - public static ICollection NotNullOrEmpty(ICollection? value, [InvokerParameterName][NotNull] string parameterName) + public static ICollection NotNullOrEmpty( + [System.Diagnostics.CodeAnalysis.NotNull] ICollection? value, + [InvokerParameterName][NotNull] string parameterName) { if (value == null || value.Count <= 0) { @@ -339,7 +341,7 @@ public static class Check } public static T NotDefaultOrNull( - T? value, + [System.Diagnostics.CodeAnalysis.NotNull] T? value, [InvokerParameterName][NotNull] string parameterName) where T : struct { From 3c8944cec5d67a5699270977453a84c3b5250b28 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 18 Jun 2024 10:21:58 +0800 Subject: [PATCH 03/14] Use DbFunction for global query filters. Resolve #20062 --- ...EfCoreDbContextOptionsBuilderExtensions.cs | 21 ++++++ .../AbpEfCoreModelBuilderExtensions.cs | 68 +++++++++++++++++++ .../Abp/EntityFrameworkCore/AbpDbContext.cs | 31 +++++++-- .../AbpDbContextOptionsExtension.cs | 62 +++++++++++++++++ .../AbpEntityQueryProvider.cs | 49 +++++++++++++ .../DbContextOptionsFactory.cs | 6 ++ .../AbpCompiledQueryCacheKeyGenerator.cs | 59 ++++++++++++++++ .../AbpEfCoreCurrentDbContext.cs | 21 ++++++ .../AbpEfCoreDataFilterDbFunctionMethods.cs | 21 ++++++ .../AbpEfCoreGlobalFilterOptions.cs | 11 +++ .../IAbpEfCoreDbFunctionContext.cs | 16 +++++ .../Abp/Auditing/AbpAuditingTestModule.cs | 2 +- .../AbpEntityFrameworkCoreTestModule.cs | 4 +- 13 files changed, 364 insertions(+), 7 deletions(-) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreDbContextOptionsBuilderExtensions.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsExtension.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityQueryProvider.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpCompiledQueryCacheKeyGenerator.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreCurrentDbContext.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/IAbpEfCoreDbFunctionContext.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreDbContextOptionsBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreDbContextOptionsBuilderExtensions.cs new file mode 100644 index 0000000000..8c4b636d9e --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class AbpEfCoreDbContextOptionsBuilderExtensions +{ + public static DbContextOptionsBuilder AddAbpDbContextOptionsExtension(this DbContextOptionsBuilder optionsBuilder) + { + ((IDbContextOptionsBuilderInfrastructure) optionsBuilder).AddOrUpdateExtension(new AbpDbContextOptionsExtension()); + return optionsBuilder; + } + + public static DbContextOptionsBuilder AddAbpDbContextOptionsExtension(this DbContextOptionsBuilder optionsBuilder) + where TContext : DbContext + { + ((IDbContextOptionsBuilderInfrastructure) optionsBuilder).AddOrUpdateExtension(new AbpDbContextOptionsExtension()); + return optionsBuilder; + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs new file mode 100644 index 0000000000..19ad9e0267 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs @@ -0,0 +1,68 @@ +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; +using Volo.Abp.MultiTenancy; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class AbpEfCoreModelBuilderExtensions +{ + public static ModelBuilder ConfigureSoftDeleteDbFunction(this ModelBuilder modelBuilder, MethodInfo methodInfo, AbpEfCoreCurrentDbContext abpEfCoreCurrentDbContext) + { + modelBuilder.HasDbFunction(methodInfo) + .HasTranslation(args => + { + // (bool isDeleted, bool boolParam) + var isDeleted = args[0]; + var boolParam = args[1]; + + if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled() == true) + { + // IsDeleted == false + return new SqlBinaryExpression( + ExpressionType.Equal, + isDeleted, + new SqlConstantExpression(Expression.Constant(false), boolParam.TypeMapping), + boolParam.Type, + boolParam.TypeMapping); + } + + // empty where sql + return new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping); + }); + + return modelBuilder; + } + + public static ModelBuilder ConfigureMultiTenantDbFunction(this ModelBuilder modelBuilder, MethodInfo methodInfo, AbpEfCoreCurrentDbContext abpEfCoreCurrentDbContext) + { + modelBuilder.HasDbFunction(methodInfo) + .HasTranslation(args => + { + // (Guid? tenantId, int? currentTenantId) + var tenantId = args[0]; + var currentTenantId = args[1]; + var boolParam = args[2]; + + if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled() == true) + { + // TenantId == CurrentTenantId + return new SqlBinaryExpression( + ExpressionType.Equal, + tenantId, + currentTenantId, + boolParam.Type, + boolParam.TypeMapping); + } + + // empty where sql + return new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping); + }); + + return modelBuilder; + } +} 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 138bf82ec4..c1c87cdead 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -11,11 +11,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; @@ -24,6 +25,7 @@ using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Repositories; using Volo.Abp.EntityFrameworkCore.ChangeTrackers; using Volo.Abp.EntityFrameworkCore.EntityHistory; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.EventBus.Distributed; @@ -37,7 +39,7 @@ using Volo.Abp.Uow; namespace Volo.Abp.EntityFrameworkCore; -public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, ITransientDependency +public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, IAbpEfCoreDbFunctionContext, ITransientDependency where TDbContext : DbContext { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } = default!; @@ -78,6 +80,8 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, public IOptions Options => LazyServiceProvider.LazyGetRequiredService>(); + public IOptions GlobalFilterOptions => LazyServiceProvider.LazyGetRequiredService>(); + private static readonly MethodInfo ConfigureBasePropertiesMethodInfo = typeof(AbpDbContext) .GetMethod( @@ -713,7 +717,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { if (mutableEntityType.BaseType == null && ShouldFilterEntity(mutableEntityType)) { - var filterExpression = CreateFilterExpression(); + var filterExpression = CreateFilterExpression(modelBuilder); if (filterExpression != null) { modelBuilder.Entity().HasAbpQueryFilter(filterExpression); @@ -782,22 +786,41 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return false; } - protected virtual Expression>? CreateFilterExpression() + protected virtual Expression>? CreateFilterExpression(ModelBuilder modelBuilder) where TEntity : class { Expression>? expression = null; + var abpEfCoreCurrentDbContext = this.GetService(); if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, "IsDeleted"); + + if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction) + { + expression = e => AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilter(((ISoftDelete)e).IsDeleted, true); + modelBuilder.ConfigureSoftDeleteDbFunction(AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, abpEfCoreCurrentDbContext); + } } if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) { Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, "TenantId") == CurrentTenantId; + + if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction) + { + multiTenantFilter = e => AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilter(((IMultiTenant)e).TenantId, CurrentTenantId, true); + modelBuilder.ConfigureMultiTenantDbFunction(AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilterMethodInfo, abpEfCoreCurrentDbContext); + } + expression = expression == null ? multiTenantFilter : QueryFilterExpressionHelper.CombineExpressions(expression, multiTenantFilter); } return expression; } + + public virtual string GetCompiledQueryCacheKey() + { + return $"{CurrentTenantId?.ToString() ?? "Null"}{IsSoftDeleteFilterEnabled}:{IsMultiTenantFilterEnabled}"; + } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsExtension.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsExtension.cs new file mode 100644 index 0000000000..d9dc4cef9f --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsExtension.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; + +namespace Volo.Abp.EntityFrameworkCore; + +public class AbpDbContextOptionsExtension : IDbContextOptionsExtension +{ + public void ApplyServices(IServiceCollection services) + { + var serviceDescriptor = services.FirstOrDefault(x => x.ServiceType == typeof(ICompiledQueryCacheKeyGenerator)); + if (serviceDescriptor != null && serviceDescriptor.ImplementationType != null) + { + services.Remove(serviceDescriptor); + services.AddScoped(serviceDescriptor.ImplementationType); + services.Add(ServiceDescriptor.Scoped(provider => + ActivatorUtilities.CreateInstance(provider, + provider.GetRequiredService(serviceDescriptor.ImplementationType) + .As()))); + } + + services.Replace(ServiceDescriptor.Scoped()); + services.AddSingleton(typeof(AbpEfCoreCurrentDbContext)); + } + + public void Validate(IDbContextOptions options) + { + } + + public DbContextOptionsExtensionInfo Info => new AbpOptionsExtensionInfo(this); + + private class AbpOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public AbpOptionsExtensionInfo(IDbContextOptionsExtension extension) + : base(extension) + { + } + + public override bool IsDatabaseProvider => false; + + public override int GetServiceProviderHashCode() + { + return 0; + } + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) + { + return other is AbpOptionsExtensionInfo; + } + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override string LogFragment => "AbpOptionsExtension"; + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityQueryProvider.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityQueryProvider.cs new file mode 100644 index 0000000000..665757e17b --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityQueryProvider.cs @@ -0,0 +1,49 @@ +using System.Linq.Expressions; +using System.Threading; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Query.Internal; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; + +namespace Volo.Abp.EntityFrameworkCore; + +#pragma warning disable EF1001 +public class AbpEntityQueryProvider : EntityQueryProvider +{ + protected AbpEfCoreCurrentDbContext AbpEfCoreCurrentDbContext { get; } + protected ICurrentDbContext CurrentDbContext { get; } + + public AbpEntityQueryProvider( + IQueryCompiler queryCompiler, + AbpEfCoreCurrentDbContext abpEfCoreCurrentDbContext, + ICurrentDbContext currentDbContext) + : base(queryCompiler) + { + AbpEfCoreCurrentDbContext = abpEfCoreCurrentDbContext; + CurrentDbContext = currentDbContext; + } + + public override object Execute(Expression expression) + { + using (AbpEfCoreCurrentDbContext.Use(CurrentDbContext.Context as IAbpEfCoreDbFunctionContext)) + { + return base.Execute(expression); + } + } + + public override TResult Execute(Expression expression) + { + using (AbpEfCoreCurrentDbContext.Use(CurrentDbContext.Context as IAbpEfCoreDbFunctionContext)) + { + return base.Execute(expression); + } + } + + public override TResult ExecuteAsync(Expression expression, CancellationToken cancellationToken = new CancellationToken()) + { + using (AbpEfCoreCurrentDbContext.Use(CurrentDbContext.Context as IAbpEfCoreDbFunctionContext)) + { + return base.ExecuteAsync(expression, cancellationToken); + } + } +} +#pragma warning restore EF1001 diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs index 1b3a6f48b5..113a4aa86c 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.MultiTenancy; namespace Volo.Abp.EntityFrameworkCore.DependencyInjection; @@ -27,6 +28,11 @@ public static class DbContextOptionsFactory PreConfigure(options, context); Configure(options, context); + if (serviceProvider.GetRequiredService>().Value.UseDbFunction) + { + context.DbContextOptions.AddAbpDbContextOptionsExtension(); + } + return context.DbContextOptions.Options; } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpCompiledQueryCacheKeyGenerator.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpCompiledQueryCacheKeyGenerator.cs new file mode 100644 index 0000000000..e43ae1e04d --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpCompiledQueryCacheKeyGenerator.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Query; + +namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; + +public class AbpCompiledQueryCacheKeyGenerator : ICompiledQueryCacheKeyGenerator +{ + protected ICompiledQueryCacheKeyGenerator InnerCompiledQueryCacheKeyGenerator { get; } + protected ICurrentDbContext CurrentContext { get; } + + public AbpCompiledQueryCacheKeyGenerator( + ICompiledQueryCacheKeyGenerator innerCompiledQueryCacheKeyGenerator, + ICurrentDbContext currentContext) + { + InnerCompiledQueryCacheKeyGenerator = innerCompiledQueryCacheKeyGenerator; + CurrentContext = currentContext; + } + + public virtual object GenerateCacheKey(Expression query, bool async) + { + var cacheKey = InnerCompiledQueryCacheKeyGenerator.GenerateCacheKey(query, async); + if (CurrentContext.Context is IAbpEfCoreDbFunctionContext abpEfCoreDbFunctionContext) + { + return new AbpCompiledQueryCacheKey(cacheKey, abpEfCoreDbFunctionContext.GetCompiledQueryCacheKey()); + } + + return cacheKey; + } + + private readonly struct AbpCompiledQueryCacheKey : IEquatable + { + private readonly object _compiledQueryCacheKey; + private readonly string _currentFilterCacheKey; + + public AbpCompiledQueryCacheKey(object compiledQueryCacheKey, string currentFilterCacheKey) + { + _compiledQueryCacheKey = compiledQueryCacheKey; + _currentFilterCacheKey = currentFilterCacheKey; + } + + public override bool Equals(object? obj) + { + return obj is AbpCompiledQueryCacheKey key && Equals(key); + } + + public bool Equals(AbpCompiledQueryCacheKey other) + { + return _compiledQueryCacheKey.Equals(other._compiledQueryCacheKey) && + _currentFilterCacheKey == other._currentFilterCacheKey; + } + + public override int GetHashCode() + { + return HashCode.Combine(_compiledQueryCacheKey, _currentFilterCacheKey); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreCurrentDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreCurrentDbContext.cs new file mode 100644 index 0000000000..c3a29b6d70 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreCurrentDbContext.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading; + +namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; + +public class AbpEfCoreCurrentDbContext +{ + private readonly AsyncLocal _current = new AsyncLocal(); + + public IAbpEfCoreDbFunctionContext? Context => _current.Value; + + public IDisposable Use(IAbpEfCoreDbFunctionContext? context) + { + var previousValue = Context; + _current.Value = context; + return new DisposeAction(() => + { + _current.Value = previousValue; + }); + } +} 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 new file mode 100644 index 0000000000..e7d672e9ba --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreDataFilterDbFunctionMethods.cs @@ -0,0 +1,21 @@ +using System; +using System.Reflection; + +namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; + +public static class AbpEfCoreDataFilterDbFunctionMethods +{ + public static bool SoftDeleteFilter(bool isDeleted, bool boolParam) + { + throw new NotSupportedException("This method should be replaced by the database function call."); + } + + public static MethodInfo SoftDeleteFilterMethodInfo => typeof(AbpEfCoreDataFilterDbFunctionMethods).GetMethod(nameof(SoftDeleteFilter))!; + + public static bool MultiTenantFilter(Guid? tenantId, Guid? currentTenantId, bool boolParam) + { + throw new NotSupportedException("This method should be replaced by the database function call."); + } + + public static MethodInfo MultiTenantFilterMethodInfo => typeof(AbpEfCoreDataFilterDbFunctionMethods).GetMethod(nameof(MultiTenantFilter))!; +} 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 new file mode 100644 index 0000000000..180ae849e7 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/AbpEfCoreGlobalFilterOptions.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; + +public class AbpEfCoreGlobalFilterOptions +{ + public bool UseDbFunction { get; set; } + + public AbpEfCoreGlobalFilterOptions() + { + UseDbFunction = true; + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/IAbpEfCoreDbFunctionContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/IAbpEfCoreDbFunctionContext.cs new file mode 100644 index 0000000000..85096e9e0a --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/GlobalFilters/IAbpEfCoreDbFunctionContext.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; + +public interface IAbpEfCoreDbFunctionContext +{ + IAbpLazyServiceProvider LazyServiceProvider { get; set; } + + ICurrentTenant CurrentTenant { get; } + + IDataFilter DataFilter { get; } + + string GetCompiledQueryCacheKey(); +} diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs index 4fe3ae9c16..4e14dbc262 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs @@ -70,7 +70,7 @@ public class AbpAuditingTestModule : AbpModule connection.Open(); using (var context = new AbpAuditingTestDbContext(new DbContextOptionsBuilder() - .UseSqlite(connection).Options)) + .UseSqlite(connection).AddAbpDbContextOptionsExtension().Options)) { context.GetService().CreateTables(); } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index 5e81aa1a7d..a13aea2945 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -72,7 +72,7 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule { options.Configure(abpDbContextConfigurationContext => { - abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection).AddAbpDbContextOptionsExtension(); }); }); } @@ -101,7 +101,7 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule var connection = new AbpUnitTestSqliteConnection("Data Source=:memory:"); connection.Open(); - using (var context = new TestMigrationsDbContext(new DbContextOptionsBuilder().UseSqlite(connection).Options)) + using (var context = new TestMigrationsDbContext(new DbContextOptionsBuilder().UseSqlite(connection).AddAbpDbContextOptionsExtension().Options)) { context.GetService().CreateTables(); context.Database.ExecuteSqlRaw( From c76b9e6c4ae227a70ce7a7fdf02722fb5ea5fce7 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 18 Jun 2024 12:46:15 +0800 Subject: [PATCH 04/14] Set `UseDbFunction` to `false` by default. --- .../MySQL/AbpEntityFrameworkCoreMySQLModule.cs | 8 +++++++- .../AbpEntityFrameworkCoreOracleDevartModule.cs | 6 ++++++ .../Oracle/AbpEntityFrameworkCoreOracleModule.cs | 6 ++++++ .../AbpEntityFrameworkCorePostgreSqlModule.cs | 8 +++++++- .../AbpEntityFrameworkCoreSqlServerModule.cs | 8 +++++++- .../Sqlite/AbpEntityFrameworkCoreSqliteModule.cs | 9 ++++++++- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 12 +++++++----- .../AbpEfCoreDataFilterDbFunctionMethods.cs | 8 ++++++-- .../GlobalFilters/AbpEfCoreGlobalFilterOptions.cs | 5 ----- 9 files changed, 54 insertions(+), 16 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs index 6431590230..0b493f5058 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Guids; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; +using Volo.Abp.Guids; using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.MySQL; @@ -17,5 +18,10 @@ public class AbpEntityFrameworkCoreMySQLModule : AbpModule options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString; } }); + + Configure(options => + { + options.UseDbFunction = true; + }); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs index 3b4129b272..317f664b04 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.Guids; using Volo.Abp.Modularity; @@ -18,5 +19,10 @@ public class AbpEntityFrameworkCoreOracleDevartModule : AbpModule options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary; } }); + + Configure(options => + { + options.UseDbFunction = true; + }); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs index 54627a90c0..76ffe32f7a 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.Guids; using Volo.Abp.Modularity; @@ -16,5 +17,10 @@ public class AbpEntityFrameworkCoreOracleModule : AbpModule options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary; } }); + + Configure(options => + { + options.UseDbFunction = true; + }); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs index 2b679cccef..b82b128b3b 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Guids; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; +using Volo.Abp.Guids; using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.PostgreSql; @@ -17,5 +18,10 @@ public class AbpEntityFrameworkCorePostgreSqlModule : AbpModule options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString; } }); + + Configure(options => + { + options.UseDbFunction = true; + }); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs index 8186809846..acebc98d86 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Guids; +using Volo.Abp.EntityFrameworkCore.GlobalFilters; +using Volo.Abp.Guids; using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.SqlServer; @@ -17,5 +18,10 @@ public class AbpEntityFrameworkCoreSqlServerModule : AbpModule options.DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd; } }); + + Configure(options => + { + options.UseDbFunction = true; + }); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs index e772546154..52415d73b1 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs @@ -1,3 +1,4 @@ +using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.Sqlite; @@ -7,5 +8,11 @@ namespace Volo.Abp.EntityFrameworkCore.Sqlite; )] public class AbpEntityFrameworkCoreSqliteModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.UseDbFunction = true; + }); + } } 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 c1c87cdead..6f45b75dc3 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -103,9 +103,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, BindingFlags.Instance | BindingFlags.NonPublic )!; + protected readonly DbContextOptions DbContextOptions; + protected AbpDbContext(DbContextOptions options) : base(options) { + DbContextOptions = options; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -791,15 +794,14 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { Expression>? expression = null; - var abpEfCoreCurrentDbContext = this.GetService(); if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, "IsDeleted"); - if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction) + if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction && DbContextOptions.FindExtension() != null) { expression = e => AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilter(((ISoftDelete)e).IsDeleted, true); - modelBuilder.ConfigureSoftDeleteDbFunction(AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, abpEfCoreCurrentDbContext); + modelBuilder.ConfigureSoftDeleteDbFunction(AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, this.GetService()); } } @@ -807,10 +809,10 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, "TenantId") == CurrentTenantId; - if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction) + if (LazyServiceProvider != null && GlobalFilterOptions.Value.UseDbFunction && DbContextOptions.FindExtension() != null) { multiTenantFilter = e => AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilter(((IMultiTenant)e).TenantId, CurrentTenantId, true); - modelBuilder.ConfigureMultiTenantDbFunction(AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilterMethodInfo, abpEfCoreCurrentDbContext); + modelBuilder.ConfigureMultiTenantDbFunction(AbpEfCoreDataFilterDbFunctionMethods.MultiTenantFilterMethodInfo, this.GetService()); } expression = expression == null ? multiTenantFilter : QueryFilterExpressionHelper.CombineExpressions(expression, multiTenantFilter); 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 e7d672e9ba..35694433ed 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,16 +5,20 @@ 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'." + + "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." ; + public static bool SoftDeleteFilter(bool isDeleted, bool boolParam) { - throw new NotSupportedException("This method should be replaced by the database function call."); + throw new NotSupportedException(NotSupportedExceptionMessage); } public static MethodInfo SoftDeleteFilterMethodInfo => typeof(AbpEfCoreDataFilterDbFunctionMethods).GetMethod(nameof(SoftDeleteFilter))!; public static bool MultiTenantFilter(Guid? tenantId, Guid? currentTenantId, bool boolParam) { - throw new NotSupportedException("This method should be replaced by the database function call."); + throw new NotSupportedException(NotSupportedExceptionMessage); } public static MethodInfo MultiTenantFilterMethodInfo => typeof(AbpEfCoreDataFilterDbFunctionMethods).GetMethod(nameof(MultiTenantFilter))!; 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 180ae849e7..e1641cd2a1 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 @@ -3,9 +3,4 @@ namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; public class AbpEfCoreGlobalFilterOptions { public bool UseDbFunction { get; set; } - - public AbpEfCoreGlobalFilterOptions() - { - UseDbFunction = true; - } } From 109ca428bb9237c5b863c9a2361b36d2ae490783 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 18 Jun 2024 16:19:24 +0800 Subject: [PATCH 05/14] 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; } } From 82bd494c39360f142a1cbbb139a84e773b8a9ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SAL=C4=B0H=20=C3=96ZKARA?= Date: Fri, 21 Jun 2024 16:48:16 +0300 Subject: [PATCH 06/14] Update Static-JavaScript-Proxies.md --- docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md b/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md index 20b87aa89b..2ad46a2f9d 100644 --- a/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md +++ b/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md @@ -49,7 +49,7 @@ Open a command-line terminal in the root folder of your web project (`.csproj`) abp generate-proxy -t js -u https://localhost:53929/ ```` -> If you haven't installed yet, you should install the [ABP CLI](../CLI.md). Change the example URL to your application's root URL. +> If you haven't installed yet, you should install the [ABP CLI](../../CLI.md). Change the example URL to your application's root URL. This command should generate the following files under the `ClientProxies` folder: @@ -66,7 +66,7 @@ acme.bookStore.authors.author.get = function(id, ajaxParams) { }; ```` -> `generate-proxy` command generates proxies for only the APIs you've defined in your application (assumes `app` as the module name). If you are developing a modular application, you can specify the `-m` (or `--module`) parameter to specify the module you want to generate proxies. See the *generate-proxy* section in the [ABP CLI](../CLI.md) documentation for other options. +> `generate-proxy` command generates proxies for only the APIs you've defined in your application (assumes `app` as the module name). If you are developing a modular application, you can specify the `-m` (or `--module`) parameter to specify the module you want to generate proxies. See the *generate-proxy* section in the [ABP CLI](../../CLI.md) documentation for other options. ### Using the Proxy Functions @@ -154,4 +154,4 @@ See the [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) documentation for all * [Dynamic JavaScript API Client Proxies](Dynamic-JavaScript-Proxies.md) * [Auto API Controllers](../../API/Auto-API-Controllers.md) -* [Web Application Development Tutorial](../../Tutorials/Part-1.md) \ No newline at end of file +* [Web Application Development Tutorial](../../Tutorials/Part-1.md) From 8cf62b74ffd9882a7c61610fcbc39c45431fcecb Mon Sep 17 00:00:00 2001 From: Berkan Sasmaz Date: Mon, 24 Jun 2024 15:30:30 +0300 Subject: [PATCH 07/14] Add url information to comment approve page --- .../Pages/CmsKit/Comments/Approve/index.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js index 23a84dee34..f8aa2ba80d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js @@ -57,8 +57,8 @@ orderable: false, visible: abp.auth.isGranted('CmsKit.Comments.Update'), render: function (data, type, row) { - let approveButton = $(``); - let rejectButton = $(``); + let approveButton = $(``); + let rejectButton = $(``); let buttons = []; buttons.push(approveButton); buttons.push(rejectButton); @@ -99,6 +99,16 @@ return ""; } }, + { + title: l("URL"), + data: "url", + render: function (data, type, row) { + if (data !== null) { + return ''; + } + return ""; + } + }, { title: l("Text"), data: "text", From 503929f0b3760d810c81664248951b2c31cfef91 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Tue, 25 Jun 2024 13:12:48 +0300 Subject: [PATCH 08/14] update: add triggers prop for FormPropTooltip --- .../components/extensible/src/lib/models/form-props.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts index ec1c825e9a..ca96d02010 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts @@ -30,6 +30,7 @@ export interface FormPropGroup { export interface FormPropTooltip { text: string; placement?: 'top' | 'end' | 'bottom' | 'start'; + triggers?: string; } export class GroupedFormPropList { @@ -121,7 +122,10 @@ export class FormProp extends Prop { export class FormPropData extends PropData { getInjected: PropData['getInjected']; - constructor(injector: Injector, public readonly record: R) { + constructor( + injector: Injector, + public readonly record: R, + ) { super(); this.getInjected = injector.get.bind(injector); From d20c71630fa244a72ddb4481b56fc943eb84067e Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Tue, 25 Jun 2024 13:13:26 +0300 Subject: [PATCH 09/14] add: triggers property option for the button tooltip --- .../src/lib/components/grid-actions/grid-actions.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html index 9991336bf9..ae70cb77e0 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html @@ -64,6 +64,7 @@ [style]="action.btnStyle" [ngbTooltip]="action.tooltip.text | abpLocalization" [placement]="action.tooltip.placement || 'auto'" + [triggers]="action.tooltip.triggers || 'hover focus'" container="body" > Date: Tue, 25 Jun 2024 15:30:49 +0300 Subject: [PATCH 10/14] remove: the unnecessary prop for the tooltip trigger --- .../packages/components/extensible/src/lib/models/form-props.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts index ca96d02010..c1b8cacbe2 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/form-props.ts @@ -30,7 +30,6 @@ export interface FormPropGroup { export interface FormPropTooltip { text: string; placement?: 'top' | 'end' | 'bottom' | 'start'; - triggers?: string; } export class GroupedFormPropList { From 9078b7fcb40afbbf731ecae8854ed7c0e8d6489c Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Tue, 25 Jun 2024 15:34:37 +0300 Subject: [PATCH 11/14] update: the triggers prop with a static hover value --- .../src/lib/components/grid-actions/grid-actions.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html index ae70cb77e0..606a6de12a 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html @@ -64,7 +64,7 @@ [style]="action.btnStyle" [ngbTooltip]="action.tooltip.text | abpLocalization" [placement]="action.tooltip.placement || 'auto'" - [triggers]="action.tooltip.triggers || 'hover focus'" + triggers="hover" container="body" > Date: Wed, 26 Jun 2024 10:54:25 +0800 Subject: [PATCH 12/14] Remove Blazor.Client project if UI is not Blazor --- .../Microservice/MicroserviceTemplateBase.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs index a70f1db649..0d66937841 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs @@ -172,6 +172,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor", null, "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Client", null, + "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Client")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.ProductService.Blazor", "/services/product/MyCompanyName.MyProjectName.ProductService.sln", "/services/product/src/MyCompanyName.MyProjectName.ProductService.Blazor")); @@ -217,6 +219,13 @@ public abstract class MicroserviceTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor", null, "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor")); + + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Client", + "/apps/blazor/MyCompanyName.MyProjectName.Blazor.sln", + "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Client")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Client", + null, + "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Client")); steps.Add(new RemoveProjectFromTyeStep("blazor")); steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.Blazor.Server", @@ -230,6 +239,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo case UiFramework.NotSpecified: steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor", null, "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Client", null, + "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Client")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server", null, "/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Server")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.ProductService.Blazor", From 6b00799526b98b64fb26cf5ac126f171c768390d Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 26 Jun 2024 13:20:09 +0800 Subject: [PATCH 13/14] Update `datatables.net` to `2.0.2`. https://github.com/abpframework/abp/pull/19340 --- npm/packs/datatables.net-bs5/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/packs/datatables.net-bs5/package.json b/npm/packs/datatables.net-bs5/package.json index 03bcece395..d37ed04f99 100644 --- a/npm/packs/datatables.net-bs5/package.json +++ b/npm/packs/datatables.net-bs5/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@abp/datatables.net": "~8.2.0-rc.5", - "datatables.net-bs5": "^1.11.4" + "datatables.net-bs5": "^2.0.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", From 93600533769b1d98c7fdbf9cca074cf17ac621d3 Mon Sep 17 00:00:00 2001 From: selman koc <64414348+skoc10@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:43:55 +0300 Subject: [PATCH 14/14] Update version to release 8.2.0 --- common.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.props b/common.props index d40d32471c..fb57fe4818 100644 --- a/common.props +++ b/common.props @@ -1,8 +1,8 @@ latest - 8.2.0-rc.5 - 3.2.0-rc.5 + 8.2.0 + 3.2.0 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/