From 0d69d40416c45de321a8996629a55584cbf8b843 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 26 Jul 2019 18:58:47 +0200 Subject: [PATCH] Make EvaluableExpressionFilter public --- ...ntityFrameworkRelationalServicesBuilder.cs | 3 +- .../RelationalEvaluatableExpressionFilter.cs | 19 +++-- ...EvaluatableExpressionFilterDependencies.cs | 77 +++++++++++++++++++ .../EntityFrameworkServicesBuilder.cs | 3 +- .../EvaluatableExpressionFilter.cs | 36 +++++++-- ...EvaluatableExpressionFilterDependencies.cs | 58 ++++++++++++++ .../EvaluatableExpressionFilterBase.cs | 33 -------- ...uatableExpressionFilterDependenciesTest.cs | 17 ++++ ...uatableExpressionFilterDependenciesTest.cs | 17 ++++ 9 files changed, 218 insertions(+), 45 deletions(-) rename src/EFCore.Relational/Query/{Internal => }/RelationalEvaluatableExpressionFilter.cs (77%) create mode 100644 src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilterDependencies.cs rename src/EFCore/Query/{Internal => }/EvaluatableExpressionFilter.cs (76%) create mode 100644 src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs delete mode 100644 src/EFCore/Query/Internal/EvaluatableExpressionFilterBase.cs create mode 100644 test/EFCore.Relational.Tests/Query/RelationalEvaluatableExpressionFilterDependenciesTest.cs create mode 100644 test/EFCore.Tests/Query/EvaluatableExpressionFilterDependenciesTest.cs diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index 8500f374cbb..8e69f2e4078 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -192,7 +192,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() - .AddDependencyScoped(); + .AddDependencyScoped() + .AddDependencyScoped(); return base.TryAddCoreServices(); } diff --git a/src/EFCore.Relational/Query/Internal/RelationalEvaluatableExpressionFilter.cs b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs similarity index 77% rename from src/EFCore.Relational/Query/Internal/RelationalEvaluatableExpressionFilter.cs rename to src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs index cd954c8119e..735d36be2d8 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalEvaluatableExpressionFilter.cs +++ b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Linq.Expressions; @@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.EntityFrameworkCore.Query.Internal +namespace Microsoft.EntityFrameworkCore.Query { /// /// @@ -33,13 +33,22 @@ public class RelationalEvaluatableExpressionFilter : EvaluatableExpressionFilter /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public RelationalEvaluatableExpressionFilter([NotNull] IModel model) + public RelationalEvaluatableExpressionFilter( + [NotNull] EvaluatableExpressionFilterDependencies dependencies, + [NotNull] RelationalEvaluatableExpressionFilterDependencies relationalDependencies) + : base(dependencies) { - Check.NotNull(model, nameof(model)); + Check.NotNull(relationalDependencies, nameof(relationalDependencies)); - _model = model; + RelationalDependencies = relationalDependencies; + _model = relationalDependencies.Model; } + /// + /// Dependencies used to create a + /// + protected virtual RelationalEvaluatableExpressionFilterDependencies RelationalDependencies { get; } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilterDependencies.cs b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilterDependencies.cs new file mode 100644 index 00000000000..140c022cfec --- /dev/null +++ b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilterDependencies.cs @@ -0,0 +1,77 @@ +// 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 JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Utilities; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// + /// Service dependencies parameter class for + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// Do not construct instances of this class directly from either provider or application code as the + /// constructor signature may change as new dependencies are added. Instead, use this type in + /// your constructor so that an instance will be created and injected automatically by the + /// dependency injection container. To create an instance with some dependent services replaced, + /// first resolve the object from the dependency injection container, then replace selected + /// services using the 'With...' methods. Do not call the constructor at any point in this process. + /// + /// + /// The service lifetime is . This means that each + /// instance will use its own instance of this service. + /// The implementation may depend on other services registered with any lifetime. + /// The implementation does not need to be thread-safe. + /// + /// + public sealed class RelationalEvaluatableExpressionFilterDependencies + { + /// + /// + /// Creates the service dependencies parameter object for a . + /// + /// + /// Do not call this constructor directly from either provider or application code as it may change + /// as new dependencies are added. Instead, use this type in your constructor so that an instance + /// will be created and injected automatically by the dependency injection container. To create + /// an instance with some dependent services replaced, first resolve the object from the dependency + /// injection container, then replace selected services using the 'With...' methods. Do not call + /// the constructor at any point in this process. + /// + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + /// + [EntityFrameworkInternal] + public RelationalEvaluatableExpressionFilterDependencies([NotNull] IModel model) + { + Check.NotNull(model, nameof(model)); + + Model = model; + } + + /// + /// The model used with this . + /// + public IModel Model { 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 RelationalEvaluatableExpressionFilterDependencies With([NotNull] IModel model) + => new RelationalEvaluatableExpressionFilterDependencies(model); + } +} diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index 321cfdb6e58..2e280ad3d9e 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -288,7 +288,8 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() - .AddDependencyScoped(); + .AddDependencyScoped() + .AddDependencyScoped(); ServiceCollectionMap.TryAddSingleton( new RegisteredServices(ServiceCollectionMap.ServiceCollection.Select(s => s.ServiceType))); diff --git a/src/EFCore/Query/Internal/EvaluatableExpressionFilter.cs b/src/EFCore/Query/EvaluatableExpressionFilter.cs similarity index 76% rename from src/EFCore/Query/Internal/EvaluatableExpressionFilter.cs rename to src/EFCore/Query/EvaluatableExpressionFilter.cs index 72a72d2ba67..95dec1e1951 100644 --- a/src/EFCore/Query/Internal/EvaluatableExpressionFilter.cs +++ b/src/EFCore/Query/EvaluatableExpressionFilter.cs @@ -1,12 +1,15 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Expressions; using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.EntityFrameworkCore.Query.Internal +namespace Microsoft.EntityFrameworkCore.Query { /// /// @@ -22,7 +25,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal /// The implementation does not need to be thread-safe. /// /// - public class EvaluatableExpressionFilter : EvaluatableExpressionFilterBase + public class EvaluatableExpressionFilter : IEvaluatableExpressionFilter { // This methods are non-deterministic and result varies based on time of running the query. // Hence we don't evaluate them. See issue#2069 @@ -54,13 +57,36 @@ 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. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The dependencies to use. + public EvaluatableExpressionFilter( + [NotNull] EvaluatableExpressionFilterDependencies dependencies) + { + Check.NotNull(dependencies, nameof(dependencies)); + + Dependencies = dependencies; + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override bool IsEvaluatableExpression(Expression expression) + public virtual bool IsEvaluatableExpression(Expression expression) { switch (expression) { @@ -91,7 +117,7 @@ public override bool IsEvaluatableExpression(Expression expression) break; } - return base.IsEvaluatableExpression(expression); + return true; } } } diff --git a/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs b/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs new file mode 100644 index 00000000000..5a2f53eccea --- /dev/null +++ b/src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs @@ -0,0 +1,58 @@ +// 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 Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// + /// Service dependencies parameter class for + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// Do not construct instances of this class directly from either provider or application code as the + /// constructor signature may change as new dependencies are added. Instead, use this type in + /// your constructor so that an instance will be created and injected automatically by the + /// dependency injection container. To create an instance with some dependent services replaced, + /// first resolve the object from the dependency injection container, then replace selected + /// services using the 'With...' methods. Do not call the constructor at any point in this process. + /// + /// + /// The service lifetime is . This means that each + /// instance will use its own instance of this service. + /// The implementation may depend on other services registered with any lifetime. + /// The implementation does not need to be thread-safe. + /// + /// + public sealed class EvaluatableExpressionFilterDependencies + { + /// + /// + /// Creates the service dependencies parameter object for a . + /// + /// + /// Do not call this constructor directly from either provider or application code as it may change + /// as new dependencies are added. Instead, use this type in your constructor so that an instance + /// will be created and injected automatically by the dependency injection container. To create + /// an instance with some dependent services replaced, first resolve the object from the dependency + /// injection container, then replace selected services using the 'With...' methods. Do not call + /// the constructor at any point in this process. + /// + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + /// + [EntityFrameworkInternal] + public EvaluatableExpressionFilterDependencies() + { + } + } +} diff --git a/src/EFCore/Query/Internal/EvaluatableExpressionFilterBase.cs b/src/EFCore/Query/Internal/EvaluatableExpressionFilterBase.cs deleted file mode 100644 index 90d811d2b04..00000000000 --- a/src/EFCore/Query/Internal/EvaluatableExpressionFilterBase.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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.Linq.Expressions; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.EntityFrameworkCore.Query.Internal -{ - /// - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - /// - /// The service lifetime is . This means that each - /// instance will use its own instance of this service. - /// The implementation may depend on other services registered with any lifetime. - /// The implementation does not need to be thread-safe. - /// - /// - public abstract class EvaluatableExpressionFilterBase : IEvaluatableExpressionFilter - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual bool IsEvaluatableExpression(Expression expression) => true; - } -} diff --git a/test/EFCore.Relational.Tests/Query/RelationalEvaluatableExpressionFilterDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/RelationalEvaluatableExpressionFilterDependenciesTest.cs new file mode 100644 index 00000000000..bcf295391ad --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/RelationalEvaluatableExpressionFilterDependenciesTest.cs @@ -0,0 +1,17 @@ +// 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 Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; + +namespace Microsoft.EntityFrameworkCore.Query +{ + public class RelationalEvaluatableExpressionFilterDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Tests/Query/EvaluatableExpressionFilterDependenciesTest.cs b/test/EFCore.Tests/Query/EvaluatableExpressionFilterDependenciesTest.cs new file mode 100644 index 00000000000..63132f1bb85 --- /dev/null +++ b/test/EFCore.Tests/Query/EvaluatableExpressionFilterDependenciesTest.cs @@ -0,0 +1,17 @@ +// 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 Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; + +namespace Microsoft.EntityFrameworkCore.Query +{ + public class EvaluatableExpressionFilterDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + InMemoryTestHelpers.Instance.TestDependenciesClone(); + } + } +}