diff --git a/src/EntityFrameworkCore.Projections/Infrastructure/Internal/CustomQueryTranslationPreprocessorFactory.cs b/src/EntityFrameworkCore.Projections/Infrastructure/Internal/CustomQueryTranslationPreprocessorFactory.cs new file mode 100644 index 0000000..fa25c3b --- /dev/null +++ b/src/EntityFrameworkCore.Projections/Infrastructure/Internal/CustomQueryTranslationPreprocessorFactory.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using EntityFrameworkCore.Projections.Services; +using Microsoft.EntityFrameworkCore.Query; + +namespace EntityFrameworkCore.Projections.Infrastructure.Internal +{ + public class CustomQueryTranslationPreprocessorFactory : IQueryTranslationPreprocessorFactory + { + readonly IQueryTranslationPreprocessorFactory _decoratedFactory; + readonly QueryTranslationPreprocessorDependencies _queryTranslationPreprocessorDependencies; + readonly ProjectableExpressionReplacer _projectableExpressionReplacer = new(); + + public CustomQueryTranslationPreprocessorFactory(IQueryTranslationPreprocessorFactory decoratedFactory, QueryTranslationPreprocessorDependencies queryTranslationPreprocessorDependencies) + { + _decoratedFactory = decoratedFactory; + _queryTranslationPreprocessorDependencies = queryTranslationPreprocessorDependencies; + } + + public QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext) + => new CustomQueryTranslationPreprocessor(_queryTranslationPreprocessorDependencies, queryCompilationContext, _projectableExpressionReplacer); + } + + public class CustomQueryTranslationPreprocessor : QueryTranslationPreprocessor + { + readonly ProjectableExpressionReplacer _projectableExpressionReplacer; + + public CustomQueryTranslationPreprocessor(QueryTranslationPreprocessorDependencies dependencies, QueryCompilationContext queryCompilationContext, ProjectableExpressionReplacer projectableExpressionReplacer) : base(dependencies, queryCompilationContext) + { + _projectableExpressionReplacer = projectableExpressionReplacer; + } + + public override Expression Process(Expression query) + => base.Process(_projectableExpressionReplacer.Visit(query)); + } +} diff --git a/src/EntityFrameworkCore.Projections/Infrastructure/Internal/ProjectionOptionsExtension.cs b/src/EntityFrameworkCore.Projections/Infrastructure/Internal/ProjectionOptionsExtension.cs index f752032..394ba44 100644 --- a/src/EntityFrameworkCore.Projections/Infrastructure/Internal/ProjectionOptionsExtension.cs +++ b/src/EntityFrameworkCore.Projections/Infrastructure/Internal/ProjectionOptionsExtension.cs @@ -2,8 +2,10 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -22,23 +24,35 @@ namespace EntityFrameworkCore.Projections.Infrastructure.Internal [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "Needed")] public void ApplyServices(IServiceCollection services) { - var queryCompilerRegistration = services.FirstOrDefault(x => x.ServiceType == typeof(IQueryCompiler)); - if (queryCompilerRegistration?.ImplementationType is null) + static object CreateTargetInstance(IServiceProvider services, ServiceDescriptor descriptor) { - throw new InvalidOperationException("No queryCompiler is configured yet. Please make sure to configure a database provider first"); ; + if (descriptor.ImplementationInstance is not null) + return descriptor.ImplementationInstance; + + if (descriptor.ImplementationFactory is not null) + return descriptor.ImplementationFactory(services); + + Debug.Assert(descriptor.ImplementationType is not null); + + return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType!); } - // Ensure that we can still resolve this queryCompiler - services.Add(new ServiceDescriptor(queryCompilerRegistration.ImplementationType, queryCompilerRegistration.ImplementationType, queryCompilerRegistration.Lifetime)); - services.Remove(queryCompilerRegistration); + var targetDescriptor = services.FirstOrDefault(x => x.ServiceType == typeof(IQueryTranslationPreprocessorFactory)); + if (targetDescriptor is null) + { + throw new InvalidOperationException("No QueryTranslationPreprocessorFactory is configured yet. Please make sure to configure a database provider first"); ; + } - services.Add(new ServiceDescriptor( - typeof(IQueryCompiler), - serviceProvider => new WrappedQueryCompiler((IQueryCompiler)serviceProvider.GetRequiredService(queryCompilerRegistration.ImplementationType)), - queryCompilerRegistration.Lifetime + var decoratorObjectFactory = ActivatorUtilities.CreateFactory(typeof(CustomQueryTranslationPreprocessorFactory), new [] { targetDescriptor.ServiceType }); + + services.Replace(ServiceDescriptor.Describe( + targetDescriptor.ServiceType, + serviceProvider => decoratorObjectFactory(serviceProvider, new[] { CreateTargetInstance(serviceProvider, targetDescriptor) }), + targetDescriptor.Lifetime )); } + public void Validate(IDbContextOptions options) { } diff --git a/src/EntityFrameworkCore.Projections/Infrastructure/Internal/WrappedQueryCompiler.cs b/src/EntityFrameworkCore.Projections/Infrastructure/Internal/WrappedQueryCompiler.cs deleted file mode 100644 index 74f285c..0000000 --- a/src/EntityFrameworkCore.Projections/Infrastructure/Internal/WrappedQueryCompiler.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EntityFrameworkCore.Projections.Services; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Query.Internal; - -namespace EntityFrameworkCore.Projections.Infrastructure.Internal -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "Needed")] - public sealed class WrappedQueryCompiler : IQueryCompiler - { - readonly ProjectableExpressionReplacer _projectionExpressionReplacer = new(); - readonly IQueryCompiler _queryCompiler; - - public WrappedQueryCompiler(IQueryCompiler queryCompiler) - { - _queryCompiler = queryCompiler; - } - - public Func CreateCompiledAsyncQuery(Expression query) - => _queryCompiler.CreateCompiledAsyncQuery(PatchQuery(query)); - - public Func CreateCompiledQuery(Expression query) - => _queryCompiler.CreateCompiledQuery(PatchQuery(query)); - - public TResult Execute(Expression query) - => _queryCompiler.Execute(PatchQuery(query)); - - public TResult ExecuteAsync(Expression query, CancellationToken cancellationToken) - => _queryCompiler.ExecuteAsync(PatchQuery(query), cancellationToken); - - Expression PatchQuery(Expression expression) - => _projectionExpressionReplacer.Visit(expression); - - } -}