From ed25ca554198a7c3e20dcef494ffd7c4d53759d7 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 29 Jul 2019 13:20:32 -0700 Subject: [PATCH] Add dependency objects for query services Fixes #16788 --- ...yableMethodTranslatingExpressionVisitor.cs | 4 +- ...thodTranslatingExpressionVisitorFactory.cs | 5 + ...osShapedQueryCompilingExpressionVisitor.cs | 4 +- ...dQueryCompilingExpressionVisitorFactory.cs | 10 +- ...yableMethodTranslatingExpressionVisitor.cs | 14 ++- ...thodTranslatingExpressionVisitorFactory.cs | 11 +- .../InMemoryShapedQueryExpressionVisitor.cs | 4 +- ...moryShapedQueryExpressionVisitorFactory.cs | 13 +- .../Internal/InMemoryShapedQueryOptimizer.cs | 5 + .../InMemoryShapedQueryOptimizerFactory.cs | 9 +- .../IInMemoryIntegerValueGenerator.cs | 22 ++++ .../Internal/InMemoryIntegerValueGenerator.cs | 17 --- ...ntityFrameworkRelationalServicesBuilder.cs | 5 + .../Internal/QuerySqlGeneratorFactory.cs | 18 +-- ...thodTranslatingExpressionVisitorFactory.cs | 22 ++-- ...onalShapedQueryExpressionVisitorFactory.cs | 26 ++-- ...ueryOptimizingExpressionVisitorsFactory.cs | 17 ++- ...lSqlTranslatingExpressionVisitorFactory.cs | 116 +---------------- ...ingExpressionVisitorFactoryDependencies.cs | 108 ++++++++++++++++ .../Query/QuerySqlGenerator.cs | 9 +- .../Query/QuerySqlGeneratorDependencies.cs | 93 ++++++++++++++ .../Query/RelationalQueryContext.cs | 25 ++-- .../RelationalQueryContextDependencies.cs | 94 ++++++++++++++ .../Query/RelationalQueryContextFactory.cs | 21 ++-- ...yableMethodTranslatingExpressionVisitor.cs | 21 +++- ...ranslatingExpressionVisitorDependencies.cs | 92 ++++++++++++++ ...alShapedQueryCompilingExpressionVisitor.cs | 18 +-- ...yCompilingExpressionVisitorDependencies.cs | 119 ++++++++++++++++++ .../Query/RelationalShapedQueryOptimizer.cs | 13 +- ...ationalShapedQueryOptimizerDependencies.cs | 77 ++++++++++++ ...lationalSqlTranslatingExpressionVisitor.cs | 17 +-- .../Query/SqlExpressionVisitor.cs | 9 ++ ...rchConditionConvertingExpressionVisitor.cs | 5 +- .../Internal/SqlServerQuerySqlGenerator.cs | 6 +- .../SqlServerQuerySqlGeneratorFactory.cs | 13 +- ...ShapedQueryOptimizingExpressionVisitors.cs | 13 +- ...ueryOptimizingExpressionVisitorsFactory.cs | 21 +++- ...qlServerSqlTranslatingExpressionVisitor.cs | 10 +- ...rSqlTranslatingExpressionVisitorFactory.cs | 23 ++-- .../Query/Internal/SqliteQuerySqlGenerator.cs | 6 +- .../SqliteQuerySqlGeneratorFactory.cs | 13 +- .../SqliteSqlTranslatingExpressionVisitor.cs | 8 +- ...eSqlTranslatingExpressionVisitorFactory.cs | 23 ++-- .../EntityFrameworkServicesBuilder.cs | 4 + .../Query/Internal/QueryOptimizerFactory.cs | 9 +- .../Internal/ShapedQueryOptimizerFactory.cs | 9 +- src/EFCore/Query/QueryOptimizer.cs | 8 +- .../Query/QueryOptimizerDependencies.cs | 58 +++++++++ ...yableMethodTranslatingExpressionVisitor.cs | 9 +- ...ranslatingExpressionVisitorDependencies.cs | 57 +++++++++ .../ShapedQueryCompilingExpressionVisitor.cs | 16 ++- ...yCompilingExpressionVisitorDependencies.cs | 77 ++++++++++++ src/EFCore/Query/ShapedQueryOptimizer.cs | 7 ++ .../Query/ShapedQueryOptimizerDependencies.cs | 58 +++++++++ .../QuerySqlGeneratorDependenciesTest.cs | 17 +++ .../RelationalQueryContextDependenciesTest.cs | 17 +++ ...latingExpressionVisitorDependenciesTest.cs | 17 +++ ...pilingExpressionVisitorDependenciesTest.cs | 17 +++ ...nalShapedQueryOptimizerDependenciesTest.cs | 17 +++ .../Query/QueryOptimizerDependenciesTest.cs | 17 +++ ...latingExpressionVisitorDependenciesTest.cs | 17 +++ ...pilingExpressionVisitorDependenciesTest.cs | 17 +++ ...ryOptimizerDependenciesDependenciesTest.cs | 17 +++ 63 files changed, 1302 insertions(+), 342 deletions(-) create mode 100644 src/EFCore.InMemory/ValueGeneration/Internal/IInMemoryIntegerValueGenerator.cs create mode 100644 src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactoryDependencies.cs create mode 100644 src/EFCore.Relational/Query/QuerySqlGeneratorDependencies.cs create mode 100644 src/EFCore.Relational/Query/RelationalQueryContextDependencies.cs create mode 100644 src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependencies.cs create mode 100644 src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitorDependencies.cs create mode 100644 src/EFCore.Relational/Query/RelationalShapedQueryOptimizerDependencies.cs create mode 100644 src/EFCore/Query/QueryOptimizerDependencies.cs create mode 100644 src/EFCore/Query/QueryableMethodTranslatingExpressionVisitorDependencies.cs create mode 100644 src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs create mode 100644 src/EFCore/Query/ShapedQueryOptimizerDependencies.cs create mode 100644 test/EFCore.Relational.Tests/Query/QuerySqlGeneratorDependenciesTest.cs create mode 100644 test/EFCore.Relational.Tests/Query/RelationalQueryContextDependenciesTest.cs create mode 100644 test/EFCore.Relational.Tests/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependenciesTest.cs create mode 100644 test/EFCore.Relational.Tests/Query/RelationalShapedQueryCompilingExpressionVisitorDependenciesTest.cs create mode 100644 test/EFCore.Relational.Tests/Query/RelationalShapedQueryOptimizerDependenciesTest.cs create mode 100644 test/EFCore.Tests/Query/QueryOptimizerDependenciesTest.cs create mode 100644 test/EFCore.Tests/Query/QueryableMethodTranslatingExpressionVisitorDependenciesTest.cs create mode 100644 test/EFCore.Tests/Query/ShapedQueryCompilingExpressionVisitorDependenciesTest.cs create mode 100644 test/EFCore.Tests/Query/ShapedQueryOptimizerDependenciesDependenciesTest.cs diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs index 9846d9fd3d6..2cfad91bad0 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query; @@ -32,11 +33,12 @@ public class CosmosQueryableMethodTranslatingExpressionVisitor : QueryableMethod /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public CosmosQueryableMethodTranslatingExpressionVisitor( + [NotNull] QueryableMethodTranslatingExpressionVisitorDependencies dependencies, IModel model, ISqlExpressionFactory sqlExpressionFactory, IMemberTranslatorProvider memberTranslatorProvider, IMethodCallTranslatorProvider methodCallTranslatorProvider) - : base(subquery: false) + : base(dependencies, subquery: false) { _model = model; _sqlExpressionFactory = sqlExpressionFactory; diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitorFactory.cs index 6732a77a209..50571bb3c90 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitorFactory.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitorFactory.cs @@ -1,6 +1,7 @@ // 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.Metadata; using Microsoft.EntityFrameworkCore.Query; @@ -14,6 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal /// public class CosmosQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory { + private readonly QueryableMethodTranslatingExpressionVisitorDependencies _dependencies; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly IMemberTranslatorProvider _memberTranslatorProvider; private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider; @@ -25,10 +27,12 @@ public class CosmosQueryableMethodTranslatingExpressionVisitorFactory : IQueryab /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public CosmosQueryableMethodTranslatingExpressionVisitorFactory( + [NotNull] QueryableMethodTranslatingExpressionVisitorDependencies dependencies, ISqlExpressionFactory sqlExpressionFactory, IMemberTranslatorProvider memberTranslatorProvider, IMethodCallTranslatorProvider methodCallTranslatorProvider) { + _dependencies = dependencies; _sqlExpressionFactory = sqlExpressionFactory; _memberTranslatorProvider = memberTranslatorProvider; _methodCallTranslatorProvider = methodCallTranslatorProvider; @@ -43,6 +47,7 @@ public CosmosQueryableMethodTranslatingExpressionVisitorFactory( public virtual QueryableMethodTranslatingExpressionVisitor Create(IModel model) { return new CosmosQueryableMethodTranslatingExpressionVisitor( + _dependencies, model, _sqlExpressionFactory, _memberTranslatorProvider, diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs index e46a6684fd0..56ee4362b5c 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs @@ -43,10 +43,10 @@ public class CosmosShapedQueryCompilingExpressionVisitor : ShapedQueryCompilingE /// public CosmosShapedQueryCompilingExpressionVisitor( QueryCompilationContext queryCompilationContext, - IEntityMaterializerSource entityMaterializerSource, + ShapedQueryCompilingExpressionVisitorDependencies dependencies, ISqlExpressionFactory sqlExpressionFactory, IQuerySqlGeneratorFactory querySqlGeneratorFactory) - : base(queryCompilationContext, entityMaterializerSource) + : base(queryCompilationContext, dependencies) { _sqlExpressionFactory = sqlExpressionFactory; _querySqlGeneratorFactory = querySqlGeneratorFactory; diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitorFactory.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitorFactory.cs index 483ddf075eb..e68a29036be 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitorFactory.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitorFactory.cs @@ -1,7 +1,6 @@ // 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.Metadata; using Microsoft.EntityFrameworkCore.Query; namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal @@ -14,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal /// public class CosmosShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory { - private readonly IEntityMaterializerSource _entityMaterializerSource; + private readonly ShapedQueryCompilingExpressionVisitorDependencies _dependencies; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; @@ -24,11 +23,12 @@ public class CosmosShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCo /// 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 CosmosShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSource entityMaterializerSource, + public CosmosShapedQueryCompilingExpressionVisitorFactory( + ShapedQueryCompilingExpressionVisitorDependencies dependencies, ISqlExpressionFactory sqlExpressionFactory, IQuerySqlGeneratorFactory querySqlGeneratorFactory) { - _entityMaterializerSource = entityMaterializerSource; + _dependencies = dependencies; _sqlExpressionFactory = sqlExpressionFactory; _querySqlGeneratorFactory = querySqlGeneratorFactory; } @@ -42,7 +42,7 @@ public CosmosShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSou public virtual ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) => new CosmosShapedQueryCompilingExpressionVisitor( queryCompilationContext, - _entityMaterializerSource, + _dependencies, _sqlExpressionFactory, _querySqlGeneratorFactory); } diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs index 5c94db0b269..c973c4aa18a 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs @@ -17,8 +17,10 @@ public class InMemoryQueryableMethodTranslatingExpressionVisitor : QueryableMeth private readonly InMemoryProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor; private readonly IModel _model; - public InMemoryQueryableMethodTranslatingExpressionVisitor(IModel model) - : base(subquery: false) + public InMemoryQueryableMethodTranslatingExpressionVisitor( + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, + IModel model) + : base(dependencies, subquery: false) { _expressionTranslator = new InMemoryExpressionTranslatingExpressionVisitor(this); _projectionBindingExpressionVisitor = new InMemoryProjectionBindingExpressionVisitor(this, _expressionTranslator); @@ -26,9 +28,10 @@ public InMemoryQueryableMethodTranslatingExpressionVisitor(IModel model) } public InMemoryQueryableMethodTranslatingExpressionVisitor( + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, IModel model, InMemoryExpressionTranslatingExpressionVisitor expressionTranslator) - : base(subquery: true) + : base(dependencies, subquery: true) { _expressionTranslator = expressionTranslator; _projectionBindingExpressionVisitor = new InMemoryProjectionBindingExpressionVisitor(this, expressionTranslator); @@ -41,8 +44,9 @@ private static Type CreateTransparentIdentifierType(Type outerType, Type innerTy public override ShapedQueryExpression TranslateSubquery(Expression expression) { return (ShapedQueryExpression)new InMemoryQueryableMethodTranslatingExpressionVisitor( - _model, - _expressionTranslator) + Dependencies, + _model, + _expressionTranslator) .Visit(expression); } diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs index cbaa18fcf85..4ad98f686bb 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs @@ -1,6 +1,7 @@ // 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.Metadata; using Microsoft.EntityFrameworkCore.Query; @@ -8,7 +9,15 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal { public class InMemoryQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory { + private readonly QueryableMethodTranslatingExpressionVisitorDependencies _dependencies; + + public InMemoryQueryableMethodTranslatingExpressionVisitorFactory( + [NotNull] QueryableMethodTranslatingExpressionVisitorDependencies dependencies) + { + _dependencies = dependencies; + } + public virtual QueryableMethodTranslatingExpressionVisitor Create(IModel model) - => new InMemoryQueryableMethodTranslatingExpressionVisitor(model); + => new InMemoryQueryableMethodTranslatingExpressionVisitor(_dependencies, model); } } diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitor.cs index 5f3607ff398..7dfd50d02f1 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitor.cs @@ -26,8 +26,8 @@ private static readonly ConstructorInfo _valueBufferConstructor public InMemoryShapedQueryCompilingExpressionVisitor( QueryCompilationContext queryCompilationContext, - IEntityMaterializerSource entityMaterializerSource) - : base(queryCompilationContext, entityMaterializerSource) + ShapedQueryCompilingExpressionVisitorDependencies dependencies) + : base(queryCompilationContext, dependencies) { _contextType = queryCompilationContext.ContextType; _logger = queryCompilationContext.Logger; diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs index cafca00480c..a14f31d592f 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs @@ -1,26 +1,21 @@ // 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.Metadata; using Microsoft.EntityFrameworkCore.Query; namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal { public class InMemoryShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory { - private readonly IEntityMaterializerSource _entityMaterializerSource; + private readonly ShapedQueryCompilingExpressionVisitorDependencies _dependencies; - public InMemoryShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSource entityMaterializerSource) + public InMemoryShapedQueryCompilingExpressionVisitorFactory(ShapedQueryCompilingExpressionVisitorDependencies dependencies) { - _entityMaterializerSource = entityMaterializerSource; + _dependencies = dependencies; } public virtual ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) - { - return new InMemoryShapedQueryCompilingExpressionVisitor( - queryCompilationContext, - _entityMaterializerSource); - } + => new InMemoryShapedQueryCompilingExpressionVisitor(queryCompilationContext, _dependencies); } } diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizer.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizer.cs index 6bb2c2c5bff..a0c7a548306 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizer.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizer.cs @@ -8,6 +8,11 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal { public class InMemoryShapedQueryOptimizer : ShapedQueryOptimizer { + public InMemoryShapedQueryOptimizer(ShapedQueryOptimizerDependencies dependencies) + : base(dependencies) + { + } + public override Expression Visit(Expression query) { query = base.Visit(query); diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizerFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizerFactory.cs index 25392ddd9a5..aecdb6ddd40 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizerFactory.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryOptimizerFactory.cs @@ -7,9 +7,16 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal { public class InMemoryShapedQueryOptimizerFactory : IShapedQueryOptimizerFactory { + private readonly ShapedQueryOptimizerDependencies _dependencies; + + public InMemoryShapedQueryOptimizerFactory(ShapedQueryOptimizerDependencies dependencies) + { + _dependencies = dependencies; + } + public virtual ShapedQueryOptimizer Create(QueryCompilationContext queryCompilationContext) { - return new InMemoryShapedQueryOptimizer(); + return new InMemoryShapedQueryOptimizer(_dependencies); } } } diff --git a/src/EFCore.InMemory/ValueGeneration/Internal/IInMemoryIntegerValueGenerator.cs b/src/EFCore.InMemory/ValueGeneration/Internal/IInMemoryIntegerValueGenerator.cs new file mode 100644 index 00000000000..c4aaf136c89 --- /dev/null +++ b/src/EFCore.InMemory/ValueGeneration/Internal/IInMemoryIntegerValueGenerator.cs @@ -0,0 +1,22 @@ +// 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. + +namespace Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.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. + /// + public interface IInMemoryIntegerValueGenerator + { + /// + /// 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. + /// + void Bump(object[] row); + } +} diff --git a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs index 2fe350d73f8..001323b1d24 100644 --- a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs +++ b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs @@ -9,23 +9,6 @@ namespace Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.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. - /// - public interface IInMemoryIntegerValueGenerator - { - /// - /// 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. - /// - void Bump(object[] row); - } - /// /// 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/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index 8e69f2e4078..3e8e85ca41c 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -184,6 +184,10 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() + .AddDependencySingleton() + .AddDependencySingleton() + .AddDependencySingleton() + .AddDependencySingleton() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() @@ -193,6 +197,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() + .AddDependencyScoped() .AddDependencyScoped(); return base.TryAddCoreServices(); diff --git a/src/EFCore.Relational/Query/Internal/QuerySqlGeneratorFactory.cs b/src/EFCore.Relational/Query/Internal/QuerySqlGeneratorFactory.cs index 0f9d5743cd4..f59687622e7 100644 --- a/src/EFCore.Relational/Query/Internal/QuerySqlGeneratorFactory.cs +++ b/src/EFCore.Relational/Query/Internal/QuerySqlGeneratorFactory.cs @@ -1,28 +1,18 @@ // 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.Storage; - namespace Microsoft.EntityFrameworkCore.Query.Internal { public class QuerySqlGeneratorFactory : IQuerySqlGeneratorFactory { - private readonly IRelationalCommandBuilderFactory _commandBuilderFactory; - private readonly ISqlGenerationHelper _sqlGenerationHelper; + private readonly QuerySqlGeneratorDependencies _dependencies; - public QuerySqlGeneratorFactory( - IRelationalCommandBuilderFactory commandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) + public QuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies) { - _commandBuilderFactory = commandBuilderFactory; - _sqlGenerationHelper = sqlGenerationHelper; + _dependencies = dependencies; } public virtual QuerySqlGenerator Create() - { - return new QuerySqlGenerator( - _commandBuilderFactory, - _sqlGenerationHelper); - } + => new QuerySqlGenerator(_dependencies); } } diff --git a/src/EFCore.Relational/Query/Internal/RelationalQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.Relational/Query/Internal/RelationalQueryableMethodTranslatingExpressionVisitorFactory.cs index c0eba591b5c..ed4ca3ac47f 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalQueryableMethodTranslatingExpressionVisitorFactory.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalQueryableMethodTranslatingExpressionVisitorFactory.cs @@ -7,23 +7,21 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal { public class RelationalQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory { - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IRelationalSqlTranslatingExpressionVisitorFactory _relationalSqlTranslatingExpressionVisitorFactory; + private readonly QueryableMethodTranslatingExpressionVisitorDependencies _dependencies; + private readonly RelationalQueryableMethodTranslatingExpressionVisitorDependencies _relationalDependencies; public RelationalQueryableMethodTranslatingExpressionVisitorFactory( - IRelationalSqlTranslatingExpressionVisitorFactory relationalSqlTranslatingExpressionVisitorFactory, - ISqlExpressionFactory sqlExpressionFactory) + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, + RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies) { - _sqlExpressionFactory = sqlExpressionFactory; - _relationalSqlTranslatingExpressionVisitorFactory = relationalSqlTranslatingExpressionVisitorFactory; + _dependencies = dependencies; + _relationalDependencies = relationalDependencies; } public virtual QueryableMethodTranslatingExpressionVisitor Create(IModel model) - { - return new RelationalQueryableMethodTranslatingExpressionVisitor( - model, - _relationalSqlTranslatingExpressionVisitorFactory, - _sqlExpressionFactory); - } + => new RelationalQueryableMethodTranslatingExpressionVisitor( + _dependencies, + _relationalDependencies, + model); } } diff --git a/src/EFCore.Relational/Query/Internal/RelationalShapedQueryExpressionVisitorFactory.cs b/src/EFCore.Relational/Query/Internal/RelationalShapedQueryExpressionVisitorFactory.cs index b8a8a2f0da6..41b333c109e 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalShapedQueryExpressionVisitorFactory.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalShapedQueryExpressionVisitorFactory.cs @@ -1,37 +1,27 @@ // 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.Storage; - namespace Microsoft.EntityFrameworkCore.Query.Internal { public class RelationalShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory { - private readonly IEntityMaterializerSource _entityMaterializerSource; - private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; + private readonly ShapedQueryCompilingExpressionVisitorDependencies _dependencies; + private readonly RelationalShapedQueryCompilingExpressionVisitorDependencies _relationalDependencies; public RelationalShapedQueryCompilingExpressionVisitorFactory( - IEntityMaterializerSource entityMaterializerSource, - IQuerySqlGeneratorFactory querySqlGeneratorFactory, - ISqlExpressionFactory sqlExpressionFactory, - IParameterNameGeneratorFactory parameterNameGeneratorFactory) + ShapedQueryCompilingExpressionVisitorDependencies dependencies, + RelationalShapedQueryCompilingExpressionVisitorDependencies relationalDependencies) { - _entityMaterializerSource = entityMaterializerSource; - _querySqlGeneratorFactory = querySqlGeneratorFactory; - _sqlExpressionFactory = sqlExpressionFactory; - _parameterNameGeneratorFactory = parameterNameGeneratorFactory; + _dependencies = dependencies; + _relationalDependencies = relationalDependencies; } public virtual ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) { return new RelationalShapedQueryCompilingExpressionVisitor( queryCompilationContext, - _entityMaterializerSource, - _querySqlGeneratorFactory, - _sqlExpressionFactory, - _parameterNameGeneratorFactory); + _dependencies, + _relationalDependencies); } } } diff --git a/src/EFCore.Relational/Query/Internal/RelationalShapedQueryOptimizingExpressionVisitorsFactory.cs b/src/EFCore.Relational/Query/Internal/RelationalShapedQueryOptimizingExpressionVisitorsFactory.cs index 096ab687ca6..be41735927e 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalShapedQueryOptimizingExpressionVisitorsFactory.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalShapedQueryOptimizingExpressionVisitorsFactory.cs @@ -6,16 +6,27 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal { public class RelationalShapedQueryOptimizerFactory : IShapedQueryOptimizerFactory { - protected virtual ISqlExpressionFactory SqlExpressionFactory { get; private set; } + private readonly ShapedQueryOptimizerDependencies _dependencies; + private readonly RelationalShapedQueryOptimizerDependencies _relationalDependencies; - public RelationalShapedQueryOptimizerFactory(ISqlExpressionFactory sqlExpressionFactory) + public RelationalShapedQueryOptimizerFactory( + ShapedQueryOptimizerDependencies dependencies, + RelationalShapedQueryOptimizerDependencies relationalDependencies, + ISqlExpressionFactory sqlExpressionFactory) { + _dependencies = dependencies; + _relationalDependencies = relationalDependencies; SqlExpressionFactory = sqlExpressionFactory; } + protected virtual ISqlExpressionFactory SqlExpressionFactory { get; } + public virtual ShapedQueryOptimizer Create(QueryCompilationContext queryCompilationContext) { - return new RelationalShapedQueryOptimizer(queryCompilationContext, SqlExpressionFactory); + return new RelationalShapedQueryOptimizer( + _dependencies, + _relationalDependencies, + queryCompilationContext); } } } diff --git a/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactory.cs b/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactory.cs index e36b2994c12..7048f470ce7 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactory.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactory.cs @@ -1,125 +1,19 @@ // 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.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 a single instance - /// is used by many instances. The implementation must be thread-safe. - /// This service cannot depend on services registered as . - /// - /// - public sealed class RelationalSqlTranslatingExpressionVisitorFactoryDependencies - { - /// - /// - /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies( - [NotNull] ISqlExpressionFactory sqlExpressionFactory, - [NotNull] IMemberTranslatorProvider memberTranslatorProvider, - [NotNull] IMethodCallTranslatorProvider methodCallTranslatorProvider) - { - Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); - Check.NotNull(memberTranslatorProvider, nameof(memberTranslatorProvider)); - Check.NotNull(methodCallTranslatorProvider, nameof(methodCallTranslatorProvider)); - - SqlExpressionFactory = sqlExpressionFactory; - MemberTranslatorProvider = memberTranslatorProvider; - MethodCallTranslatorProvider = methodCallTranslatorProvider; - } - - /// - /// The expression factory.. - /// - public ISqlExpressionFactory SqlExpressionFactory { get; } - - /// - /// The member translation provider. - /// - public IMemberTranslatorProvider MemberTranslatorProvider { get; } - - /// - /// The method-call translation provider. - /// - public IMethodCallTranslatorProvider MethodCallTranslatorProvider { 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory) - => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(sqlExpressionFactory, MemberTranslatorProvider, MethodCallTranslatorProvider); - - /// - /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] IMemberTranslatorProvider memberTranslatorProvider) - => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(SqlExpressionFactory, memberTranslatorProvider, MethodCallTranslatorProvider); - - /// - /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] IMethodCallTranslatorProvider methodCallTranslatorProvider) - => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(SqlExpressionFactory, MemberTranslatorProvider, methodCallTranslatorProvider); - } - public class RelationalSqlTranslatingExpressionVisitorFactory : IRelationalSqlTranslatingExpressionVisitorFactory { - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IMemberTranslatorProvider _memberTranslatorProvider; - private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider; + private readonly RelationalSqlTranslatingExpressionVisitorFactoryDependencies _dependencies; public RelationalSqlTranslatingExpressionVisitorFactory( [NotNull] RelationalSqlTranslatingExpressionVisitorFactoryDependencies dependencies) { - _sqlExpressionFactory = dependencies.SqlExpressionFactory; - _memberTranslatorProvider = dependencies.MemberTranslatorProvider; - _methodCallTranslatorProvider = dependencies.MethodCallTranslatorProvider; + _dependencies = dependencies; } public virtual RelationalSqlTranslatingExpressionVisitor Create( @@ -127,11 +21,9 @@ public virtual RelationalSqlTranslatingExpressionVisitor Create( QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) { return new RelationalSqlTranslatingExpressionVisitor( + _dependencies, model, - queryableMethodTranslatingExpressionVisitor, - _sqlExpressionFactory, - _memberTranslatorProvider, - _methodCallTranslatorProvider); + queryableMethodTranslatingExpressionVisitor); } } } diff --git a/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactoryDependencies.cs b/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactoryDependencies.cs new file mode 100644 index 00000000000..28b00432fdb --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/RelationalSqlTranslatingExpressionVisitorFactoryDependencies.cs @@ -0,0 +1,108 @@ +// 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.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 a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public sealed class RelationalSqlTranslatingExpressionVisitorFactoryDependencies + { + /// + /// + /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies( + [NotNull] ISqlExpressionFactory sqlExpressionFactory, + [NotNull] IMemberTranslatorProvider memberTranslatorProvider, + [NotNull] IMethodCallTranslatorProvider methodCallTranslatorProvider) + { + Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); + Check.NotNull(memberTranslatorProvider, nameof(memberTranslatorProvider)); + Check.NotNull(methodCallTranslatorProvider, nameof(methodCallTranslatorProvider)); + + SqlExpressionFactory = sqlExpressionFactory; + MemberTranslatorProvider = memberTranslatorProvider; + MethodCallTranslatorProvider = methodCallTranslatorProvider; + } + + /// + /// The expression factory.. + /// + public ISqlExpressionFactory SqlExpressionFactory { get; } + + /// + /// The member translation provider. + /// + public IMemberTranslatorProvider MemberTranslatorProvider { get; } + + /// + /// The method-call translation provider. + /// + public IMethodCallTranslatorProvider MethodCallTranslatorProvider { 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory) + => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(sqlExpressionFactory, MemberTranslatorProvider, MethodCallTranslatorProvider); + + /// + /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] IMemberTranslatorProvider memberTranslatorProvider) + => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(SqlExpressionFactory, memberTranslatorProvider, MethodCallTranslatorProvider); + + /// + /// 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 RelationalSqlTranslatingExpressionVisitorFactoryDependencies With([NotNull] IMethodCallTranslatorProvider methodCallTranslatorProvider) + => new RelationalSqlTranslatingExpressionVisitorFactoryDependencies(SqlExpressionFactory, MemberTranslatorProvider, methodCallTranslatorProvider); + } +} diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index dd404b7417c..dea8546a91e 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -38,12 +38,11 @@ public class QuerySqlGenerator : SqlExpressionVisitor { ExpressionType.Or, " | " } }; - public QuerySqlGenerator( - IRelationalCommandBuilderFactory relationalCommandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) + public QuerySqlGenerator(QuerySqlGeneratorDependencies dependencies) + : base(dependencies) { - _relationalCommandBuilderFactory = relationalCommandBuilderFactory; - _sqlGenerationHelper = sqlGenerationHelper; + _relationalCommandBuilderFactory = dependencies.RelationalCommandBuilderFactory; + _sqlGenerationHelper = dependencies.SqlGenerationHelper; } public virtual IRelationalCommand GetCommand(SelectExpression selectExpression) diff --git a/src/EFCore.Relational/Query/QuerySqlGeneratorDependencies.cs b/src/EFCore.Relational/Query/QuerySqlGeneratorDependencies.cs new file mode 100644 index 00000000000..520756cc152 --- /dev/null +++ b/src/EFCore.Relational/Query/QuerySqlGeneratorDependencies.cs @@ -0,0 +1,93 @@ +// 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.Storage; +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 a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public sealed class QuerySqlGeneratorDependencies + { + /// + /// + /// 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 QuerySqlGeneratorDependencies( + [NotNull] IRelationalCommandBuilderFactory relationalCommandBuilderFactory, + [NotNull] ISqlGenerationHelper sqlGenerationHelper) + { + Check.NotNull(relationalCommandBuilderFactory, nameof(relationalCommandBuilderFactory)); + Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper)); + + RelationalCommandBuilderFactory = relationalCommandBuilderFactory; + SqlGenerationHelper = sqlGenerationHelper; + } + + /// + /// The command-builder factory. + /// + public IRelationalCommandBuilderFactory RelationalCommandBuilderFactory { get; } + + /// + /// SQL generation helpers. + /// + public ISqlGenerationHelper SqlGenerationHelper { 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 QuerySqlGeneratorDependencies With([NotNull] IRelationalCommandBuilderFactory relationalCommandBuilderFactory) + => new QuerySqlGeneratorDependencies(relationalCommandBuilderFactory, SqlGenerationHelper); + + /// + /// 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 QuerySqlGeneratorDependencies With([NotNull] ISqlGenerationHelper sqlGenerationHelper) + => new QuerySqlGeneratorDependencies(RelationalCommandBuilderFactory, sqlGenerationHelper); + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryContext.cs b/src/EFCore.Relational/Query/RelationalQueryContext.cs index c820f1cdbe2..6601923169c 100644 --- a/src/EFCore.Relational/Query/RelationalQueryContext.cs +++ b/src/EFCore.Relational/Query/RelationalQueryContext.cs @@ -14,7 +14,7 @@ public class RelationalQueryContext : QueryContext { /// /// - /// Creates a new instance. + /// Creates a new instance. /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -22,28 +22,30 @@ public class RelationalQueryContext : QueryContext /// /// /// The dependencies to use. - /// The relational connection. - /// A factory for creating the execution strategy to use. + /// The relational-specific dependencies to use. public RelationalQueryContext( [NotNull] QueryContextDependencies dependencies, - [NotNull] IRelationalConnection connection, - [NotNull] IExecutionStrategyFactory executionStrategyFactory) + [NotNull] RelationalQueryContextDependencies relationalDependencies) : base(dependencies) { - Check.NotNull(connection, nameof(connection)); - Check.NotNull(executionStrategyFactory, nameof(executionStrategyFactory)); + Check.NotNull(relationalDependencies, nameof(relationalDependencies)); - Connection = connection; - ExecutionStrategyFactory = executionStrategyFactory; + RelationalDependencies = relationalDependencies; } + /// + /// Relational-specific dependencies. + /// + protected virtual RelationalQueryContextDependencies RelationalDependencies { get; } + /// /// Gets the active relational connection. /// /// /// The connection. /// - public virtual IRelationalConnection Connection { get; } + public virtual IRelationalConnection Connection + => RelationalDependencies.RelationalConnection; /// /// The execution strategy factory. @@ -51,6 +53,7 @@ public RelationalQueryContext( /// /// The execution strategy factory. /// - public virtual IExecutionStrategyFactory ExecutionStrategyFactory { get; } + public virtual IExecutionStrategyFactory ExecutionStrategyFactory + => RelationalDependencies.ExecutionStrategyFactory; } } diff --git a/src/EFCore.Relational/Query/RelationalQueryContextDependencies.cs b/src/EFCore.Relational/Query/RelationalQueryContextDependencies.cs new file mode 100644 index 00000000000..8253e6ebe75 --- /dev/null +++ b/src/EFCore.Relational/Query/RelationalQueryContextDependencies.cs @@ -0,0 +1,94 @@ +// 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.Storage; +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 RelationalQueryContextDependencies + { + /// + /// + /// 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 RelationalQueryContextDependencies( + [NotNull] IRelationalConnection relationalConnection, + [NotNull] IExecutionStrategyFactory executionStrategyFactory) + { + Check.NotNull(relationalConnection, nameof(relationalConnection)); + Check.NotNull(executionStrategyFactory, nameof(executionStrategyFactory)); + + RelationalConnection = relationalConnection; + ExecutionStrategyFactory = executionStrategyFactory; + } + + /// + /// The connection. + /// + public IRelationalConnection RelationalConnection { get; } + + /// + /// The execution strategy. + /// + public IExecutionStrategyFactory ExecutionStrategyFactory { 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 RelationalQueryContextDependencies With([NotNull] IRelationalConnection relationalConnection) + => new RelationalQueryContextDependencies(relationalConnection, ExecutionStrategyFactory); + + /// + /// 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 RelationalQueryContextDependencies With([NotNull] IExecutionStrategyFactory executionStrategyFactory) + => new RelationalQueryContextDependencies(RelationalConnection, executionStrategyFactory); + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryContextFactory.cs b/src/EFCore.Relational/Query/RelationalQueryContextFactory.cs index fb1ca93b3d4..1cf096875b3 100644 --- a/src/EFCore.Relational/Query/RelationalQueryContextFactory.cs +++ b/src/EFCore.Relational/Query/RelationalQueryContextFactory.cs @@ -20,31 +20,32 @@ namespace Microsoft.EntityFrameworkCore.Query /// public class RelationalQueryContextFactory : QueryContextFactory { - private readonly IRelationalConnection _connection; - /// /// Creates a new instance using the given dependencies. /// /// The dependencies to use. - /// The connection to use. - /// A factory for the execution strategy to use. + /// Relational-specific dependencies. public RelationalQueryContextFactory( [NotNull] QueryContextDependencies dependencies, - [NotNull] IRelationalConnection connection, - [NotNull] IExecutionStrategyFactory executionStrategyFactory) + [NotNull] RelationalQueryContextDependencies relationalDependencies) : base(dependencies) { - _connection = connection; - ExecutionStrategyFactory = executionStrategyFactory; + RelationalDependencies = relationalDependencies; } + /// + /// Relational-specific dependencies. + /// + protected virtual RelationalQueryContextDependencies RelationalDependencies { get; } + /// /// The execution strategy factory. /// /// /// The execution strategy factory. /// - protected virtual IExecutionStrategyFactory ExecutionStrategyFactory { get; } + protected virtual IExecutionStrategyFactory ExecutionStrategyFactory + => RelationalDependencies.ExecutionStrategyFactory; /// /// Creates a new . @@ -53,6 +54,6 @@ public RelationalQueryContextFactory( /// A QueryContext. /// public override QueryContext Create() - => new RelationalQueryContext(Dependencies, _connection, ExecutionStrategyFactory); + => new RelationalQueryContext(Dependencies, RelationalDependencies); } } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index dc067f30acc..2a6632948b4 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -26,24 +26,31 @@ public class RelationalQueryableMethodTranslatingExpressionVisitor : QueryableMe private readonly ISqlExpressionFactory _sqlExpressionFactory; public RelationalQueryableMethodTranslatingExpressionVisitor( - IModel model, - IRelationalSqlTranslatingExpressionVisitorFactory relationalSqlTranslatingExpressionVisitorFactory, - ISqlExpressionFactory sqlExpressionFactory) - : base(subquery: false) + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, + RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies, + IModel model) + : base(dependencies, subquery: false) { - _sqlTranslator = relationalSqlTranslatingExpressionVisitorFactory.Create(model, this); + RelationalDependencies = relationalDependencies; + + var sqlExpressionFactory = relationalDependencies.SqlExpressionFactory; + _sqlTranslator = relationalDependencies.RelationalSqlTranslatingExpressionVisitorFactory.Create(model, this); _weakEntityExpandingExpressionVisitor = new WeakEntityExpandingExpressionVisitor(_sqlTranslator, sqlExpressionFactory); _projectionBindingExpressionVisitor = new RelationalProjectionBindingExpressionVisitor(this, _sqlTranslator); _model = model; _sqlExpressionFactory = sqlExpressionFactory; } + protected virtual RelationalQueryableMethodTranslatingExpressionVisitorDependencies RelationalDependencies { get; } + private RelationalQueryableMethodTranslatingExpressionVisitor( + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, + RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies, IModel model, RelationalSqlTranslatingExpressionVisitor sqlTranslator, WeakEntityExpandingExpressionVisitor weakEntityExpandingExpressionVisitor, ISqlExpressionFactory sqlExpressionFactory) - : base(subquery: true) + : base(dependencies, subquery: true) { _model = model; _sqlTranslator = sqlTranslator; @@ -70,6 +77,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp public override ShapedQueryExpression TranslateSubquery(Expression expression) => (ShapedQueryExpression)new RelationalQueryableMethodTranslatingExpressionVisitor( + Dependencies, + RelationalDependencies, _model, _sqlTranslator, _weakEntityExpandingExpressionVisitor, diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependencies.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependencies.cs new file mode 100644 index 00000000000..5890186fff5 --- /dev/null +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependencies.cs @@ -0,0 +1,92 @@ +// 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.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 a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public sealed class RelationalQueryableMethodTranslatingExpressionVisitorDependencies + { + /// + /// + /// 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 RelationalQueryableMethodTranslatingExpressionVisitorDependencies( + [NotNull] IRelationalSqlTranslatingExpressionVisitorFactory relationalSqlTranslatingExpressionVisitorFactory, + [NotNull] ISqlExpressionFactory sqlExpressionFactory) + { + Check.NotNull(relationalSqlTranslatingExpressionVisitorFactory, nameof(relationalSqlTranslatingExpressionVisitorFactory)); + Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); + + RelationalSqlTranslatingExpressionVisitorFactory = relationalSqlTranslatingExpressionVisitorFactory; + SqlExpressionFactory = sqlExpressionFactory; + } + + /// + /// The SQL-translating expression visitor factory. + /// + public IRelationalSqlTranslatingExpressionVisitorFactory RelationalSqlTranslatingExpressionVisitorFactory { get; } + + /// + /// The SQL expression factory. + /// + public ISqlExpressionFactory SqlExpressionFactory { 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 RelationalQueryableMethodTranslatingExpressionVisitorDependencies With([NotNull] IRelationalSqlTranslatingExpressionVisitorFactory relationalSqlTranslatingExpressionVisitorFactory) + => new RelationalQueryableMethodTranslatingExpressionVisitorDependencies(relationalSqlTranslatingExpressionVisitorFactory, SqlExpressionFactory); + + /// + /// 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 RelationalQueryableMethodTranslatingExpressionVisitorDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory) + => new RelationalQueryableMethodTranslatingExpressionVisitorDependencies(RelationalSqlTranslatingExpressionVisitorFactory, sqlExpressionFactory); + } +} diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs index db9eb8bed74..5cccddb1e1c 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -23,20 +23,22 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor : ShapedQue public RelationalShapedQueryCompilingExpressionVisitor( QueryCompilationContext queryCompilationContext, - IEntityMaterializerSource entityMaterializerSource, - IQuerySqlGeneratorFactory querySqlGeneratorFactory, - ISqlExpressionFactory sqlExpressionFactory, - IParameterNameGeneratorFactory parameterNameGeneratorFactory) - : base(queryCompilationContext, entityMaterializerSource) + ShapedQueryCompilingExpressionVisitorDependencies dependencies, + RelationalShapedQueryCompilingExpressionVisitorDependencies relationalDependencies) + : base(queryCompilationContext, dependencies) { - _querySqlGeneratorFactory = querySqlGeneratorFactory; - _sqlExpressionFactory = sqlExpressionFactory; - _parameterNameGeneratorFactory = parameterNameGeneratorFactory; + RelationalDependencies = relationalDependencies; + + _querySqlGeneratorFactory = relationalDependencies.QuerySqlGeneratorFactory; + _sqlExpressionFactory = relationalDependencies.SqlExpressionFactory; + _parameterNameGeneratorFactory = relationalDependencies.ParameterNameGeneratorFactory; _contextType = queryCompilationContext.ContextType; _logger = queryCompilationContext.Logger; _tags = queryCompilationContext.Tags; } + protected virtual RelationalShapedQueryCompilingExpressionVisitorDependencies RelationalDependencies { get; } + protected override Expression VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression) { var selectExpression = (SelectExpression)shapedQueryExpression.QueryExpression; diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitorDependencies.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitorDependencies.cs new file mode 100644 index 00000000000..f68152ac9f1 --- /dev/null +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitorDependencies.cs @@ -0,0 +1,119 @@ +// 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.Storage; +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 RelationalShapedQueryCompilingExpressionVisitorDependencies + { + /// + /// + /// 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 RelationalShapedQueryCompilingExpressionVisitorDependencies( + [NotNull] IQuerySqlGeneratorFactory querySqlGeneratorFactory, + [NotNull] ISqlExpressionFactory sqlExpressionFactory, + [NotNull] IParameterNameGeneratorFactory parameterNameGeneratorFactory) + { + Check.NotNull(querySqlGeneratorFactory, nameof(querySqlGeneratorFactory)); + Check.NotNull(querySqlGeneratorFactory, nameof(querySqlGeneratorFactory)); + Check.NotNull(querySqlGeneratorFactory, nameof(querySqlGeneratorFactory)); + + QuerySqlGeneratorFactory = querySqlGeneratorFactory; + SqlExpressionFactory = sqlExpressionFactory; + ParameterNameGeneratorFactory = parameterNameGeneratorFactory; + } + + /// + /// The SQL generator factory. + /// + public IQuerySqlGeneratorFactory QuerySqlGeneratorFactory { get; } + + /// + /// The SQL expression factory. + /// + public ISqlExpressionFactory SqlExpressionFactory { get; } + + /// + /// The parameter name-generator factory. + /// + public IParameterNameGeneratorFactory ParameterNameGeneratorFactory { 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 RelationalShapedQueryCompilingExpressionVisitorDependencies With([NotNull] IQuerySqlGeneratorFactory querySqlGeneratorFactory) + => new RelationalShapedQueryCompilingExpressionVisitorDependencies( + querySqlGeneratorFactory, + SqlExpressionFactory, + ParameterNameGeneratorFactory); + + /// + /// 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 RelationalShapedQueryCompilingExpressionVisitorDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory) + => new RelationalShapedQueryCompilingExpressionVisitorDependencies( + QuerySqlGeneratorFactory, + sqlExpressionFactory, + ParameterNameGeneratorFactory); + + /// + /// 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 RelationalShapedQueryCompilingExpressionVisitorDependencies With([NotNull] IParameterNameGeneratorFactory parameterNameGeneratorFactory) + => new RelationalShapedQueryCompilingExpressionVisitorDependencies( + QuerySqlGeneratorFactory, + SqlExpressionFactory, + parameterNameGeneratorFactory); + } +} diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryOptimizer.cs b/src/EFCore.Relational/Query/RelationalShapedQueryOptimizer.cs index cce3887758a..267a9d92931 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryOptimizer.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryOptimizer.cs @@ -4,20 +4,27 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Query { public class RelationalShapedQueryOptimizer : ShapedQueryOptimizer { public RelationalShapedQueryOptimizer( - QueryCompilationContext queryCompilationContext, - ISqlExpressionFactory sqlExpressionFactory) + ShapedQueryOptimizerDependencies dependencies, + RelationalShapedQueryOptimizerDependencies relationalDependencies, + QueryCompilationContext queryCompilationContext) + : base(dependencies) { + RelationalDependencies = relationalDependencies; UseRelationalNulls = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).UseRelationalNulls; - SqlExpressionFactory = sqlExpressionFactory; + SqlExpressionFactory = relationalDependencies.SqlExpressionFactory; } + protected virtual RelationalShapedQueryOptimizerDependencies RelationalDependencies { get; } + protected virtual ISqlExpressionFactory SqlExpressionFactory { get; } + protected virtual bool UseRelationalNulls { get; } public override Expression Visit(Expression query) diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryOptimizerDependencies.cs b/src/EFCore.Relational/Query/RelationalShapedQueryOptimizerDependencies.cs new file mode 100644 index 00000000000..6dd6524db12 --- /dev/null +++ b/src/EFCore.Relational/Query/RelationalShapedQueryOptimizerDependencies.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.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 RelationalShapedQueryOptimizerDependencies + { + + /// + /// + /// 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 RelationalShapedQueryOptimizerDependencies( + [NotNull] ISqlExpressionFactory sqlExpressionFactory) + { + SqlExpressionFactory = sqlExpressionFactory; + Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); + } + + /// + /// The SQL expression factory. + /// + public ISqlExpressionFactory SqlExpressionFactory { 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 RelationalShapedQueryOptimizerDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory) + => new RelationalShapedQueryOptimizerDependencies(sqlExpressionFactory); + } +} diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index e4fa084e736..695b5b4983c 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; @@ -25,20 +24,22 @@ public class RelationalSqlTranslatingExpressionVisitor : ExpressionVisitor private readonly SqlTypeMappingVerifyingExpressionVisitor _sqlVerifyingExpressionVisitor; public RelationalSqlTranslatingExpressionVisitor( + RelationalSqlTranslatingExpressionVisitorFactoryDependencies dependencies, IModel model, - QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor, - ISqlExpressionFactory sqlExpressionFactory, - IMemberTranslatorProvider memberTranslatorProvider, - IMethodCallTranslatorProvider methodCallTranslatorProvider) + QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) { + Dependencies = dependencies; + _model = model; _queryableMethodTranslatingExpressionVisitor = queryableMethodTranslatingExpressionVisitor; - _sqlExpressionFactory = sqlExpressionFactory; - _memberTranslatorProvider = memberTranslatorProvider; - _methodCallTranslatorProvider = methodCallTranslatorProvider; + _sqlExpressionFactory = dependencies.SqlExpressionFactory; + _memberTranslatorProvider = dependencies.MemberTranslatorProvider; + _methodCallTranslatorProvider = dependencies.MethodCallTranslatorProvider; _sqlVerifyingExpressionVisitor = new SqlTypeMappingVerifyingExpressionVisitor(); } + protected virtual RelationalSqlTranslatingExpressionVisitorFactoryDependencies Dependencies { get; } + public virtual SqlExpression Translate(Expression expression) { var result = Visit(expression); diff --git a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs index 3672d4525e7..c099d6bce14 100644 --- a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs @@ -3,11 +3,20 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query { public abstract class SqlExpressionVisitor : ExpressionVisitor { + + protected SqlExpressionVisitor(QuerySqlGeneratorDependencies dependencies) + { + Dependencies = dependencies; + } + + protected virtual QuerySqlGeneratorDependencies Dependencies { get; } + protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) diff --git a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs index 34eab9c2287..de2df396e7f 100644 --- a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs @@ -14,7 +14,10 @@ public class SearchConditionConvertingExpressionVisitor : SqlExpressionVisitor private bool _isSearchCondition; private readonly ISqlExpressionFactory _sqlExpressionFactory; - public SearchConditionConvertingExpressionVisitor(ISqlExpressionFactory sqlExpressionFactory) + public SearchConditionConvertingExpressionVisitor( + QuerySqlGeneratorDependencies dependencies, + ISqlExpressionFactory sqlExpressionFactory) + : base(dependencies) { _sqlExpressionFactory = sqlExpressionFactory; } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index 89ca2cb8759..8c36f64d379 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -10,10 +10,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerQuerySqlGenerator : QuerySqlGenerator { - public SqlServerQuerySqlGenerator( - IRelationalCommandBuilderFactory relationalCommandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) - : base(relationalCommandBuilderFactory, sqlGenerationHelper) + public SqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies) + : base(dependencies) { } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGeneratorFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGeneratorFactory.cs index 332c2c38393..436a5ec4ac8 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGeneratorFactory.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGeneratorFactory.cs @@ -2,24 +2,19 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory { - private readonly IRelationalCommandBuilderFactory _commandBuilderFactory; - private readonly ISqlGenerationHelper _sqlGenerationHelper; + private readonly QuerySqlGeneratorDependencies _dependencies; - public SqlServerQuerySqlGeneratorFactory( - IRelationalCommandBuilderFactory commandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) + public SqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies) { - _commandBuilderFactory = commandBuilderFactory; - _sqlGenerationHelper = sqlGenerationHelper; + _dependencies = dependencies; } public virtual QuerySqlGenerator Create() - => new SqlServerQuerySqlGenerator(_commandBuilderFactory, _sqlGenerationHelper); + => new SqlServerQuerySqlGenerator(_dependencies); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitors.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitors.cs index 33a1d8097a3..a39eeaa2aeb 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitors.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitors.cs @@ -8,17 +8,22 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerShapedQueryOptimizer : RelationalShapedQueryOptimizer { + private readonly QuerySqlGeneratorDependencies _sqlGeneratorDependencies; + public SqlServerShapedQueryOptimizer( - QueryCompilationContext queryCompilationContext, - ISqlExpressionFactory sqlExpressionFactory) - : base(queryCompilationContext, sqlExpressionFactory) + ShapedQueryOptimizerDependencies dependencies, + RelationalShapedQueryOptimizerDependencies relationalDependencies, + QuerySqlGeneratorDependencies sqlGeneratorDependencies, + QueryCompilationContext queryCompilationContext) + : base(dependencies, relationalDependencies, queryCompilationContext) { + _sqlGeneratorDependencies = sqlGeneratorDependencies; } public override Expression Visit(Expression query) { query = base.Visit(query); - query = new SearchConditionConvertingExpressionVisitor(SqlExpressionFactory).Visit(query); + query = new SearchConditionConvertingExpressionVisitor(_sqlGeneratorDependencies, SqlExpressionFactory).Visit(query); query = new SqlExpressionOptimizingVisitor(SqlExpressionFactory, UseRelationalNulls).Visit(query); return query; diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitorsFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitorsFactory.cs index 7b0fb6b2ca0..184e6d6201c 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitorsFactory.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerShapedQueryOptimizingExpressionVisitorsFactory.cs @@ -7,16 +7,25 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerShapedQueryOptimizerFactory : IShapedQueryOptimizerFactory { - private readonly ISqlExpressionFactory _sqlExpressionFactory; + private readonly QuerySqlGeneratorDependencies _sqlGeneratorDependencies; + private readonly ShapedQueryOptimizerDependencies _dependencies; + private readonly RelationalShapedQueryOptimizerDependencies _relationalDependencies; - public SqlServerShapedQueryOptimizerFactory(ISqlExpressionFactory sqlExpressionFactory) + public SqlServerShapedQueryOptimizerFactory( + QuerySqlGeneratorDependencies sqlGeneratorDependencies, + ShapedQueryOptimizerDependencies dependencies, + RelationalShapedQueryOptimizerDependencies relationalDependencies) { - _sqlExpressionFactory = sqlExpressionFactory; + _sqlGeneratorDependencies = sqlGeneratorDependencies; + _dependencies = dependencies; + _relationalDependencies = relationalDependencies; } public virtual ShapedQueryOptimizer Create(QueryCompilationContext queryCompilationContext) - { - return new SqlServerShapedQueryOptimizer(queryCompilationContext, _sqlExpressionFactory); - } + => new SqlServerShapedQueryOptimizer( + _dependencies, + _relationalDependencies, + _sqlGeneratorDependencies, + queryCompilationContext); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs index 1ba2aeaffbb..27ef140009c 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs @@ -34,14 +34,12 @@ private static readonly HashSet _arithmeticOperatorTypes private readonly ISqlExpressionFactory _sqlExpressionFactory; public SqlServerSqlTranslatingExpressionVisitor( + RelationalSqlTranslatingExpressionVisitorFactoryDependencies dependencies, IModel model, - QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor, - ISqlExpressionFactory sqlExpressionFactory, - IMemberTranslatorProvider memberTranslatorProvider, - IMethodCallTranslatorProvider methodCallTranslatorProvider) - : base(model, queryableMethodTranslatingExpressionVisitor, sqlExpressionFactory, memberTranslatorProvider, methodCallTranslatorProvider) + QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) + : base(dependencies, model, queryableMethodTranslatingExpressionVisitor) { - _sqlExpressionFactory = sqlExpressionFactory; + _sqlExpressionFactory = dependencies.SqlExpressionFactory; } protected override Expression VisitBinary(BinaryExpression binaryExpression) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitorFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitorFactory.cs index a29038b7169..c223cbb3279 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitorFactory.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitorFactory.cs @@ -1,6 +1,7 @@ // 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.Metadata; using Microsoft.EntityFrameworkCore.Query; @@ -8,30 +9,20 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerSqlTranslatingExpressionVisitorFactory : IRelationalSqlTranslatingExpressionVisitorFactory { - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IMemberTranslatorProvider _memberTranslatorProvider; - private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider; + private readonly RelationalSqlTranslatingExpressionVisitorFactoryDependencies _dependencies; public SqlServerSqlTranslatingExpressionVisitorFactory( - ISqlExpressionFactory sqlExpressionFactory, - IMemberTranslatorProvider memberTranslatorProvider, - IMethodCallTranslatorProvider methodCallTranslatorProvider) + [NotNull] RelationalSqlTranslatingExpressionVisitorFactoryDependencies dependencies) { - _sqlExpressionFactory = sqlExpressionFactory; - _memberTranslatorProvider = memberTranslatorProvider; - _methodCallTranslatorProvider = methodCallTranslatorProvider; + _dependencies = dependencies; } public virtual RelationalSqlTranslatingExpressionVisitor Create( IModel model, QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) - { - return new SqlServerSqlTranslatingExpressionVisitor( + => new SqlServerSqlTranslatingExpressionVisitor( + _dependencies, model, - queryableMethodTranslatingExpressionVisitor, - _sqlExpressionFactory, - _memberTranslatorProvider, - _methodCallTranslatorProvider); - } + queryableMethodTranslatingExpressionVisitor); } } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs index e2baf00e439..d5cf5956ef6 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs @@ -12,10 +12,8 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal { public class SqliteQuerySqlGenerator : QuerySqlGenerator { - public SqliteQuerySqlGenerator( - IRelationalCommandBuilderFactory relationalCommandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) - : base(relationalCommandBuilderFactory, sqlGenerationHelper) + public SqliteQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies) + : base(dependencies) { } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGeneratorFactory.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGeneratorFactory.cs index 41fd0243719..a9ad0b36015 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGeneratorFactory.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGeneratorFactory.cs @@ -2,24 +2,19 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal { public class SqliteQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory { - private readonly IRelationalCommandBuilderFactory _commandBuilderFactory; - private readonly ISqlGenerationHelper _sqlGenerationHelper; + private readonly QuerySqlGeneratorDependencies _dependencies; - public SqliteQuerySqlGeneratorFactory( - IRelationalCommandBuilderFactory commandBuilderFactory, - ISqlGenerationHelper sqlGenerationHelper) + public SqliteQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies) { - _commandBuilderFactory = commandBuilderFactory; - _sqlGenerationHelper = sqlGenerationHelper; + _dependencies = dependencies; } public virtual QuerySqlGenerator Create() - => new SqliteQuerySqlGenerator(_commandBuilderFactory, _sqlGenerationHelper); + => new SqliteQuerySqlGenerator(_dependencies); } } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs index 854cd935e46..66d8004342d 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs @@ -78,12 +78,10 @@ private static readonly IReadOnlyDictionary new SqliteSqlTranslatingExpressionVisitor( + _dependencies, model, - queryableMethodTranslatingExpressionVisitor, - _sqlExpressionFactory, - _memberTranslatorProvider, - _methodCallTranslatorProvider); - } + queryableMethodTranslatingExpressionVisitor); } } diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index 2e280ad3d9e..93a69924d70 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -281,7 +281,11 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() + .AddDependencySingleton() + .AddDependencySingleton() .AddDependencyScoped() + .AddDependencyScoped() + .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() diff --git a/src/EFCore/Query/Internal/QueryOptimizerFactory.cs b/src/EFCore/Query/Internal/QueryOptimizerFactory.cs index 1d789453aa8..6a03b2fd441 100644 --- a/src/EFCore/Query/Internal/QueryOptimizerFactory.cs +++ b/src/EFCore/Query/Internal/QueryOptimizerFactory.cs @@ -5,7 +5,14 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal { public class QueryOptimizerFactory : IQueryOptimizerFactory { + private readonly QueryOptimizerDependencies _dependencies; + + public QueryOptimizerFactory(QueryOptimizerDependencies dependencies) + { + _dependencies = dependencies; + } + public virtual QueryOptimizer Create(QueryCompilationContext queryCompilationContext) - => new QueryOptimizer(queryCompilationContext); + => new QueryOptimizer(_dependencies, queryCompilationContext); } } diff --git a/src/EFCore/Query/Internal/ShapedQueryOptimizerFactory.cs b/src/EFCore/Query/Internal/ShapedQueryOptimizerFactory.cs index 4617e9bd7a3..e9ae6684d6a 100644 --- a/src/EFCore/Query/Internal/ShapedQueryOptimizerFactory.cs +++ b/src/EFCore/Query/Internal/ShapedQueryOptimizerFactory.cs @@ -5,9 +5,16 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal { public class ShapedQueryOptimizerFactory : IShapedQueryOptimizerFactory { + private readonly ShapedQueryOptimizerDependencies _dependencies; + + public ShapedQueryOptimizerFactory(ShapedQueryOptimizerDependencies dependencies) + { + _dependencies = dependencies; + } + public virtual ShapedQueryOptimizer Create(QueryCompilationContext queryCompilationContext) { - return new ShapedQueryOptimizer(); + return new ShapedQueryOptimizer(_dependencies); } } } diff --git a/src/EFCore/Query/QueryOptimizer.cs b/src/EFCore/Query/QueryOptimizer.cs index 1138dca54c4..513a35a9c39 100644 --- a/src/EFCore/Query/QueryOptimizer.cs +++ b/src/EFCore/Query/QueryOptimizer.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Internal; @@ -13,11 +14,16 @@ public class QueryOptimizer { private readonly QueryCompilationContext _queryCompilationContext; - public QueryOptimizer(QueryCompilationContext queryCompilationContext) + public QueryOptimizer( + QueryOptimizerDependencies dependencies, + QueryCompilationContext queryCompilationContext) { + Dependencies = dependencies; _queryCompilationContext = queryCompilationContext; } + protected virtual QueryOptimizerDependencies Dependencies { get; } + public virtual Expression Visit(Expression query) { query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query); diff --git a/src/EFCore/Query/QueryOptimizerDependencies.cs b/src/EFCore/Query/QueryOptimizerDependencies.cs new file mode 100644 index 00000000000..2ef40ccfcce --- /dev/null +++ b/src/EFCore/Query/QueryOptimizerDependencies.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 QueryOptimizerDependencies + { + /// + /// + /// 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 QueryOptimizerDependencies() + { + } + } +} diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index e8237a6ef8a..69cb2513b1f 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -6,8 +6,10 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Internal; +using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query { @@ -15,11 +17,16 @@ public abstract class QueryableMethodTranslatingExpressionVisitor : ExpressionVi { private readonly bool _subquery; - protected QueryableMethodTranslatingExpressionVisitor(bool subquery) + protected QueryableMethodTranslatingExpressionVisitor( + QueryableMethodTranslatingExpressionVisitorDependencies dependencies, + bool subquery) { + Dependencies = dependencies; _subquery = subquery; } + protected virtual QueryableMethodTranslatingExpressionVisitorDependencies Dependencies { get; } + protected override Expression VisitConstant(ConstantExpression constantExpression) => constantExpression.IsEntityQueryable() ? CreateShapedQueryExpression(((IQueryable)constantExpression.Value).ElementType) diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitorDependencies.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitorDependencies.cs new file mode 100644 index 00000000000..68a8ef44455 --- /dev/null +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitorDependencies.cs @@ -0,0 +1,57 @@ +// 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 a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public sealed class QueryableMethodTranslatingExpressionVisitorDependencies + { + /// + /// + /// 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 QueryableMethodTranslatingExpressionVisitorDependencies() + { + } + } +} diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs index 86fb5f879f3..877dc2f0056 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs @@ -30,20 +30,25 @@ private static readonly MethodInfo _singleOrDefaultMethodInfo private static readonly PropertyInfo _cancellationTokenMemberInfo = typeof(QueryContext).GetProperty(nameof(QueryContext.CancellationToken)); - private readonly IEntityMaterializerSource _entityMaterializerSource; private readonly Expression _cancellationTokenParameter; private readonly EntityMaterializerInjectingExpressionVisitor _entityMaterializerInjectingExpressionVisitor; protected ShapedQueryCompilingExpressionVisitor( QueryCompilationContext queryCompilationContext, - IEntityMaterializerSource entityMaterializerSource) + ShapedQueryCompilingExpressionVisitorDependencies dependencies) { - _entityMaterializerSource = entityMaterializerSource; + Dependencies = dependencies; + IsTracking = queryCompilationContext.IsTracking; + _entityMaterializerInjectingExpressionVisitor = - new EntityMaterializerInjectingExpressionVisitor(entityMaterializerSource, IsTracking); + new EntityMaterializerInjectingExpressionVisitor( + dependencies.EntityMaterializerSource, + queryCompilationContext.IsTracking); + IsAsync = queryCompilationContext.IsAsync; - if (IsAsync) + + if (queryCompilationContext.IsAsync) { _cancellationTokenParameter = Expression.MakeMemberAccess( QueryCompilationContext.QueryContextParameter, @@ -51,6 +56,7 @@ protected ShapedQueryCompilingExpressionVisitor( } } + protected virtual ShapedQueryCompilingExpressionVisitorDependencies Dependencies { get; } protected virtual bool IsTracking { get; } diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs new file mode 100644 index 00000000000..30c398fd9a0 --- /dev/null +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.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.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 ShapedQueryCompilingExpressionVisitorDependencies + { + /// + /// + /// 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 ShapedQueryCompilingExpressionVisitorDependencies( + [NotNull] IEntityMaterializerSource entityMaterializerSource) + { + Check.NotNull(entityMaterializerSource, nameof(entityMaterializerSource)); + + EntityMaterializerSource = entityMaterializerSource; + } + + /// + /// The materializer source. + /// + public IEntityMaterializerSource EntityMaterializerSource { 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 ShapedQueryCompilingExpressionVisitorDependencies With([NotNull] IEntityMaterializerSource entityMaterializerSource) + => new ShapedQueryCompilingExpressionVisitorDependencies(entityMaterializerSource); + } +} diff --git a/src/EFCore/Query/ShapedQueryOptimizer.cs b/src/EFCore/Query/ShapedQueryOptimizer.cs index ac049c0696a..0c31ddc14d1 100644 --- a/src/EFCore/Query/ShapedQueryOptimizer.cs +++ b/src/EFCore/Query/ShapedQueryOptimizer.cs @@ -7,6 +7,13 @@ namespace Microsoft.EntityFrameworkCore.Query { public class ShapedQueryOptimizer { + public ShapedQueryOptimizer(ShapedQueryOptimizerDependencies dependencies) + { + Dependencies = dependencies; + } + + protected virtual ShapedQueryOptimizerDependencies Dependencies { get; } + public virtual Expression Visit(Expression query) { return query; diff --git a/src/EFCore/Query/ShapedQueryOptimizerDependencies.cs b/src/EFCore/Query/ShapedQueryOptimizerDependencies.cs new file mode 100644 index 00000000000..7deeea2b4ec --- /dev/null +++ b/src/EFCore/Query/ShapedQueryOptimizerDependencies.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 ShapedQueryOptimizerDependencies + { + /// + /// + /// 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 ShapedQueryOptimizerDependencies() + { + } + } +} diff --git a/test/EFCore.Relational.Tests/Query/QuerySqlGeneratorDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/QuerySqlGeneratorDependenciesTest.cs new file mode 100644 index 00000000000..9ad38a0db1c --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/QuerySqlGeneratorDependenciesTest.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 QuerySqlGeneratorDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Relational.Tests/Query/RelationalQueryContextDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/RelationalQueryContextDependenciesTest.cs new file mode 100644 index 00000000000..d52da4020fc --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/RelationalQueryContextDependenciesTest.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 RelationalQueryContextDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Relational.Tests/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependenciesTest.cs new file mode 100644 index 00000000000..208df1b1cb0 --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/RelationalQueryableMethodTranslatingExpressionVisitorDependenciesTest.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 RelationalQueryableMethodTranslatingExpressionVisitorDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Relational.Tests/Query/RelationalShapedQueryCompilingExpressionVisitorDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/RelationalShapedQueryCompilingExpressionVisitorDependenciesTest.cs new file mode 100644 index 00000000000..6ee4e6d9fb5 --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/RelationalShapedQueryCompilingExpressionVisitorDependenciesTest.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 RelationalShapedQueryCompilingExpressionVisitorDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Relational.Tests/Query/RelationalShapedQueryOptimizerDependenciesTest.cs b/test/EFCore.Relational.Tests/Query/RelationalShapedQueryOptimizerDependenciesTest.cs new file mode 100644 index 00000000000..3074e727d81 --- /dev/null +++ b/test/EFCore.Relational.Tests/Query/RelationalShapedQueryOptimizerDependenciesTest.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 RelationalShapedQueryOptimizerDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + RelationalTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Tests/Query/QueryOptimizerDependenciesTest.cs b/test/EFCore.Tests/Query/QueryOptimizerDependenciesTest.cs new file mode 100644 index 00000000000..3f2dc19011e --- /dev/null +++ b/test/EFCore.Tests/Query/QueryOptimizerDependenciesTest.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 QueryOptimizerDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + InMemoryTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Tests/Query/QueryableMethodTranslatingExpressionVisitorDependenciesTest.cs b/test/EFCore.Tests/Query/QueryableMethodTranslatingExpressionVisitorDependenciesTest.cs new file mode 100644 index 00000000000..e383022195f --- /dev/null +++ b/test/EFCore.Tests/Query/QueryableMethodTranslatingExpressionVisitorDependenciesTest.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 QueryableMethodTranslatingExpressionVisitorDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + InMemoryTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Tests/Query/ShapedQueryCompilingExpressionVisitorDependenciesTest.cs b/test/EFCore.Tests/Query/ShapedQueryCompilingExpressionVisitorDependenciesTest.cs new file mode 100644 index 00000000000..458d00d8cb2 --- /dev/null +++ b/test/EFCore.Tests/Query/ShapedQueryCompilingExpressionVisitorDependenciesTest.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 ShapedQueryCompilingExpressionVisitorDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + InMemoryTestHelpers.Instance.TestDependenciesClone(); + } + } +} diff --git a/test/EFCore.Tests/Query/ShapedQueryOptimizerDependenciesDependenciesTest.cs b/test/EFCore.Tests/Query/ShapedQueryOptimizerDependenciesDependenciesTest.cs new file mode 100644 index 00000000000..a04fcb3da92 --- /dev/null +++ b/test/EFCore.Tests/Query/ShapedQueryOptimizerDependenciesDependenciesTest.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 ShapedQueryOptimizerDependenciesDependenciesTest + { + [ConditionalFact] + public void Can_use_With_methods_to_clone_and_replace_service() + { + InMemoryTestHelpers.Instance.TestDependenciesClone(); + } + } +}