diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index 8a6e7df8e4a..c0285cb1947 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -134,6 +134,7 @@ public static readonly IDictionary CoreServices { typeof(ILazyLoader), new ServiceCharacteristics(ServiceLifetime.Transient) }, { typeof(IParameterBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }, { typeof(ITypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }, + { typeof(IEvaluatableExpressionFilterPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }, { typeof(ISingletonOptions), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }, { typeof(IConventionSetPlugin), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) }, { typeof(IResettableService), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) } diff --git a/src/EFCore/Query/EvaluatableExpressionFilter.cs b/src/EFCore/Query/EvaluatableExpressionFilter.cs index 395610dbdcb..8e18416824d 100644 --- a/src/EFCore/Query/EvaluatableExpressionFilter.cs +++ b/src/EFCore/Query/EvaluatableExpressionFilter.cs @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Query /// This service cannot depend on services registered as . /// /// - public class EvaluatableExpressionFilter : IEvaluatableExpressionFilter + public class EvaluatableExpressionFilter : EvaluatableExpressionFilterBase { // This methods are non-deterministic and result varies based on time of running the query. // Hence we don't evaluate them. See issue#2069 @@ -53,11 +53,6 @@ private static readonly MethodInfo _randomNextOneArg private static readonly MethodInfo _randomNextTwoArgs = typeof(Random).GetRuntimeMethod(nameof(Random.Next), new[] { typeof(int), typeof(int) }); - /// - /// Parameter object containing dependencies for this service. - /// - protected virtual EvaluatableExpressionFilterDependencies Dependencies { get; } - /// /// /// Creates a new instance. @@ -70,10 +65,8 @@ private static readonly MethodInfo _randomNextTwoArgs /// The dependencies to use. public EvaluatableExpressionFilter( [NotNull] EvaluatableExpressionFilterDependencies dependencies) + : base(dependencies) { - Check.NotNull(dependencies, nameof(dependencies)); - - Dependencies = dependencies; } /// @@ -82,7 +75,7 @@ public EvaluatableExpressionFilter( /// The expression. /// The model. /// True if the expression can be evaluated; false otherwise. - public virtual bool IsEvaluatableExpression(Expression expression, IModel model) + public override bool IsEvaluatableExpression(Expression expression, IModel model) { Check.NotNull(expression, nameof(expression)); Check.NotNull(model, nameof(model)); @@ -116,7 +109,7 @@ public virtual bool IsEvaluatableExpression(Expression expression, IModel model) break; } - return true; + return base.IsEvaluatableExpression(expression, model); } } } diff --git a/src/EFCore/Query/EvaluatableExpressionFilterBase.cs b/src/EFCore/Query/EvaluatableExpressionFilterBase.cs new file mode 100644 index 00000000000..01ef5e99e5c --- /dev/null +++ b/src/EFCore/Query/EvaluatableExpressionFilterBase.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Utilities; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// + /// Represents a filter for evaluatable expressions. + /// + /// + /// The service lifetime is . This means a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public class EvaluatableExpressionFilterBase : IEvaluatableExpressionFilter + { + /// + /// Parameter object containing dependencies for this service. + /// + protected virtual EvaluatableExpressionFilterDependencies Dependencies { get; } + + /// + /// + /// Creates a new instance. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The dependencies to use. + public EvaluatableExpressionFilterBase( + [NotNull] EvaluatableExpressionFilterDependencies dependencies) + { + Check.NotNull(dependencies, nameof(dependencies)); + + Dependencies = dependencies; + } + + /// + /// Checks whether the given expression can be evaluated. + /// + /// The expression. + /// The model. + /// True if the expression can be evaluated; false otherwise. + public virtual bool IsEvaluatableExpression(Expression expression, IModel model) + { + Check.NotNull(expression, nameof(expression)); + Check.NotNull(model, nameof(model)); + + foreach (var plugin in Dependencies.Plugins) + { + if (!plugin.IsEvaluatableExpression(expression)) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs b/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs index 04b7276d95e..d7f7d6e9b4a 100644 --- a/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs +++ b/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore.Query @@ -50,8 +53,24 @@ public sealed class EvaluatableExpressionFilterDependencies /// /// [EntityFrameworkInternal] - public EvaluatableExpressionFilterDependencies() + public EvaluatableExpressionFilterDependencies([NotNull] IEnumerable plugins) { + Check.NotNull(plugins, nameof(plugins)); + + Plugins = plugins; } + + /// + /// Gets the plugins. + /// + public IEnumerable Plugins { get; } + + /// + /// Clones this dependency parameter object with one service replaced. + /// + /// A replacement for the current dependency of this type. + /// A new parameter object with the given service replaced. + public EvaluatableExpressionFilterDependencies With([NotNull] IEnumerable plugins) + => new EvaluatableExpressionFilterDependencies(plugins); } } diff --git a/src/EFCore/Query/IEvaluatableExpressionFilterPlugin.cs b/src/EFCore/Query/IEvaluatableExpressionFilterPlugin.cs new file mode 100644 index 00000000000..a98f53cd1e6 --- /dev/null +++ b/src/EFCore/Query/IEvaluatableExpressionFilterPlugin.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// + /// Represents a plugin evaluatable expression filter. + /// + /// + /// The service lifetime is and multiple registrations + /// are allowed. This means a single instance of each service is used by many + /// instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public interface IEvaluatableExpressionFilterPlugin + { + /// + /// Checks whether the given expression can be evaluated. + /// + /// The expression. + /// True if the expression can be evaluated; false otherwise. + bool IsEvaluatableExpression([NotNull] Expression expression); + } +}