mirror of https://github.com/abpframework/abp.git
committed by
GitHub
21 changed files with 567 additions and 25 deletions
@ -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<TContext> AddAbpDbContextOptionsExtension<TContext>(this DbContextOptionsBuilder<TContext> optionsBuilder) |
|||
where TContext : DbContext |
|||
{ |
|||
((IDbContextOptionsBuilderInfrastructure) optionsBuilder).AddOrUpdateExtension(new AbpDbContextOptionsExtension()); |
|||
return optionsBuilder; |
|||
} |
|||
} |
|||
@ -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<ISoftDelete>() == 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<IMultiTenant>() == 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; |
|||
} |
|||
} |
|||
@ -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<ICompiledQueryCacheKeyGenerator>(provider => |
|||
ActivatorUtilities.CreateInstance<AbpCompiledQueryCacheKeyGenerator>(provider, |
|||
provider.GetRequiredService(serviceDescriptor.ImplementationType) |
|||
.As<ICompiledQueryCacheKeyGenerator>()))); |
|||
} |
|||
|
|||
services.Replace(ServiceDescriptor.Scoped<IAsyncQueryProvider, AbpEntityQueryProvider>()); |
|||
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<string, string> debugInfo) |
|||
{ |
|||
} |
|||
|
|||
public override string LogFragment => "AbpOptionsExtension"; |
|||
} |
|||
} |
|||
@ -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<TResult>(Expression expression) |
|||
{ |
|||
using (AbpEfCoreCurrentDbContext.Use(CurrentDbContext.Context as IAbpEfCoreDbFunctionContext)) |
|||
{ |
|||
return base.Execute<TResult>(expression); |
|||
} |
|||
} |
|||
|
|||
public override TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken()) |
|||
{ |
|||
using (AbpEfCoreCurrentDbContext.Use(CurrentDbContext.Context as IAbpEfCoreDbFunctionContext)) |
|||
{ |
|||
return base.ExecuteAsync<TResult>(expression, cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
#pragma warning restore EF1001
|
|||
@ -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<AbpCompiledQueryCacheKey> |
|||
{ |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; |
|||
|
|||
public class AbpEfCoreCurrentDbContext |
|||
{ |
|||
private readonly AsyncLocal<IAbpEfCoreDbFunctionContext?> _current = new AsyncLocal<IAbpEfCoreDbFunctionContext?>(); |
|||
|
|||
public IAbpEfCoreDbFunctionContext? Context => _current.Value; |
|||
|
|||
public IDisposable Use(IAbpEfCoreDbFunctionContext? context) |
|||
{ |
|||
var previousValue = Context; |
|||
_current.Value = context; |
|||
return new DisposeAction(() => |
|||
{ |
|||
_current.Value = previousValue; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; |
|||
|
|||
public static class AbpEfCoreDataFilterDbFunctionMethods |
|||
{ |
|||
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." ; |
|||
|
|||
public static bool SoftDeleteFilter(bool isDeleted, bool boolParam) |
|||
{ |
|||
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(NotSupportedExceptionMessage); |
|||
} |
|||
|
|||
public static MethodInfo MultiTenantFilterMethodInfo => typeof(AbpEfCoreDataFilterDbFunctionMethods).GetMethod(nameof(MultiTenantFilter))!; |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace Volo.Abp.EntityFrameworkCore.GlobalFilters; |
|||
|
|||
public class AbpEfCoreGlobalFilterOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Use User-defined function mapping to filter data.
|
|||
/// https://learn.microsoft.com/en-us/ef/core/querying/user-defined-function-mapping
|
|||
/// </summary>
|
|||
public bool UseDbFunction { get; set; } |
|||
} |
|||
@ -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(); |
|||
} |
|||
Loading…
Reference in new issue