diff --git a/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitor.cs index 276de32cd55..b98b0ab41cb 100644 --- a/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitor.cs @@ -32,19 +32,16 @@ public class CosmosShapedQueryCompilingExpressionVisitor : ShapedQueryCompilingE private readonly IDiagnosticsLogger _logger; public CosmosShapedQueryCompilingExpressionVisitor( + QueryCompilationContext queryCompilationContext, IEntityMaterializerSource entityMaterializerSource, ISqlExpressionFactory sqlExpressionFactory, - IQuerySqlGeneratorFactory querySqlGeneratorFactory, - Type contextType, - IDiagnosticsLogger logger, - bool trackQueryResults, - bool async) - : base(entityMaterializerSource, trackQueryResults, async) + IQuerySqlGeneratorFactory querySqlGeneratorFactory) + : base(queryCompilationContext, entityMaterializerSource) { _sqlExpressionFactory = sqlExpressionFactory; _querySqlGeneratorFactory = querySqlGeneratorFactory; - _contextType = contextType; - _logger = logger; + _contextType = queryCompilationContext.ContextType; + _logger = queryCompilationContext.Logger; } protected override Expression VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression) diff --git a/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitorFactory.cs b/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitorFactory.cs index 6f9961e1a82..d65007f20a8 100644 --- a/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitorFactory.cs +++ b/src/EFCore.Cosmos/Query/Pipeline/CosmosShapedQueryCompilingExpressionVisitorFactory.cs @@ -25,13 +25,10 @@ public CosmosShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSou public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) { return new CosmosShapedQueryCompilingExpressionVisitor( + queryCompilationContext, _entityMaterializerSource, _sqlExpressionFactory, - _querySqlGeneratorFactory, - queryCompilationContext.ContextType, - queryCompilationContext.Logger, - queryCompilationContext.TrackQueryResults, - queryCompilationContext.Async); + _querySqlGeneratorFactory); } } } diff --git a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs index 549efa163d0..6d4b7a9ec09 100644 --- a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs @@ -26,14 +26,12 @@ public class InMemoryShapedQueryCompilingExpressionVisitor : ShapedQueryCompilin private readonly IDiagnosticsLogger _logger; public InMemoryShapedQueryCompilingExpressionVisitor( - IEntityMaterializerSource entityMaterializerSource, - Type contextType, - IDiagnosticsLogger logger, - bool trackQueryResults, bool async) - : base(entityMaterializerSource, trackQueryResults, async) + QueryCompilationContext queryCompilationContext, + IEntityMaterializerSource entityMaterializerSource) + : base(queryCompilationContext, entityMaterializerSource) { - _contextType = contextType; - _logger = logger; + _contextType = queryCompilationContext.ContextType; + _logger = queryCompilationContext.Logger; } protected override Expression VisitExtension(Expression extensionExpression) diff --git a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitorFactory.cs index c9ad4e98ced..e4808cdb5c3 100644 --- a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitorFactory.cs +++ b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitorFactory.cs @@ -19,11 +19,8 @@ public InMemoryShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerS public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) { return new InMemoryShapedQueryCompilingExpressionVisitor( - _entityMaterializerSource, - queryCompilationContext.ContextType, - queryCompilationContext.Logger, - queryCompilationContext.TrackQueryResults, - queryCompilationContext.Async); + queryCompilationContext, + _entityMaterializerSource); } } diff --git a/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs index ea528ba5e10..5d8597b7616 100644 --- a/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; @@ -55,14 +56,46 @@ public virtual IRelationalCommand GetCommand(SelectExpression selectExpression) } else { + GenerateTagsHeaderComment(selectExpression); + VisitSelect(selectExpression); } return _relationalCommandBuilder.Build(); } + /// + /// The default alias separator. + /// + protected virtual string AliasSeparator { get; } = " AS "; + + /// + /// The default single line comment prefix. + /// + protected virtual string SingleLineCommentToken { get; } = "--"; + protected virtual IRelationalCommandBuilder Sql => _relationalCommandBuilder; + protected virtual void GenerateTagsHeaderComment(SelectExpression selectExpression) + { + if (selectExpression.Tags.Count > 0) + { + foreach (var tag in selectExpression.Tags) + { + using (var reader = new StringReader(tag)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + _relationalCommandBuilder.Append(SingleLineCommentToken).Append(" ").AppendLine(line); + } + } + + _relationalCommandBuilder.AppendLine(); + } + } + } + protected override Expression VisitSqlFragment(SqlFragmentExpression sqlFragmentExpression) { _relationalCommandBuilder.Append(sqlFragmentExpression.Sql); @@ -94,7 +127,7 @@ protected override Expression VisitSelect(SelectExpression selectExpression) subQueryIndent.Dispose(); _relationalCommandBuilder.AppendLine() - .Append(") AS " + _sqlGenerationHelper.DelimitIdentifier(selectExpression.Alias)); + .Append(")" + AliasSeparator + _sqlGenerationHelper.DelimitIdentifier(selectExpression.Alias)); } return selectExpression; @@ -193,7 +226,7 @@ protected override Expression VisitProjection(ProjectionExpression projectionExp && !(projectionExpression.Expression is ColumnExpression column && string.Equals(column.Name, projectionExpression.Alias))) { - _relationalCommandBuilder.Append(" AS " + _sqlGenerationHelper.DelimitIdentifier(projectionExpression.Alias)); + _relationalCommandBuilder.Append(AliasSeparator + _sqlGenerationHelper.DelimitIdentifier(projectionExpression.Alias)); } return projectionExpression; @@ -248,7 +281,7 @@ protected override Expression VisitTable(TableExpression tableExpression) { _relationalCommandBuilder .Append(_sqlGenerationHelper.DelimitIdentifier(tableExpression.Table, tableExpression.Schema)) - .Append(" AS ") + .Append(AliasSeparator) .Append(_sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias)); return tableExpression; @@ -297,7 +330,8 @@ protected override Expression VisitFromSql(FromSqlExpression fromSqlExpression) GenerateFromSql(fromSqlExpression); } - _relationalCommandBuilder.Append(") AS ") + _relationalCommandBuilder.Append(")") + .Append(AliasSeparator) .Append(_sqlGenerationHelper.DelimitIdentifier(fromSqlExpression.Alias)); return fromSqlExpression; diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs index 7859bef6cb2..b228b0d9f3c 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -22,28 +22,28 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor : ShapedQue private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; + private readonly ISet _tags; public RelationalShapedQueryCompilingExpressionVisitor( + QueryCompilationContext queryCompilationContext, IEntityMaterializerSource entityMaterializerSource, IQuerySqlGeneratorFactory querySqlGeneratorFactory, ISqlExpressionFactory sqlExpressionFactory, - IParameterNameGeneratorFactory parameterNameGeneratorFactory, - Type contextType, - IDiagnosticsLogger logger, - bool trackQueryResults, - bool async) - : base(entityMaterializerSource, trackQueryResults, async) + IParameterNameGeneratorFactory parameterNameGeneratorFactory) + : base(queryCompilationContext, entityMaterializerSource) { _querySqlGeneratorFactory = querySqlGeneratorFactory; _sqlExpressionFactory = sqlExpressionFactory; _parameterNameGeneratorFactory = parameterNameGeneratorFactory; - _contextType = contextType; - _logger = logger; + _contextType = queryCompilationContext.ContextType; + _logger = queryCompilationContext.Logger; + _tags = queryCompilationContext.Tags; } protected override Expression VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression) { var selectExpression = (SelectExpression)shapedQueryExpression.QueryExpression; + selectExpression.ApplyTags(_tags); var dataReaderParameter = Expression.Parameter(typeof(DbDataReader), "dataReader"); var resultCoordinatorParameter = Expression.Parameter(typeof(ResultCoordinator), "resultCoordinator"); diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpressionVisitorFactory.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpressionVisitorFactory.cs index e35c23aa3f1..7b828473d02 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpressionVisitorFactory.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpressionVisitorFactory.cs @@ -30,14 +30,11 @@ public RelationalShapedQueryCompilingExpressionVisitorFactory( public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) { return new RelationalShapedQueryCompilingExpressionVisitor( + queryCompilationContext, _entityMaterializerSource, _querySqlGeneratorFactory, _sqlExpressionFactory, - _parameterNameGeneratorFactory, - queryCompilationContext.ContextType, - queryCompilationContext.Logger, - queryCompilationContext.TrackQueryResults, - queryCompilationContext.Async); + _parameterNameGeneratorFactory); } } } diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs index 92680ddb76b..e170d8531a0 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs @@ -3,12 +3,9 @@ using System; using System.Collections.Generic; -using System.Data.SqlTypes; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -34,11 +31,17 @@ private readonly IDictionary Projection => _projection; public IReadOnlyList Tables => _tables; public IReadOnlyList Orderings => _orderings; + public ISet Tags { get; private set; } = new HashSet(); public SqlExpression Predicate { get; private set; } public SqlExpression Limit { get; private set; } public SqlExpression Offset { get; private set; } public bool IsDistinct { get; private set; } + public void ApplyTags(ISet tags) + { + Tags = tags; + } + /// /// Marks this as representing an SQL set operation, such as a UNION. /// For regular SQL SELECT expressions, contains None. diff --git a/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpression.cs b/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpression.cs index 628fa997a23..3ef78ff1a8c 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpression.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpression.cs @@ -82,7 +82,6 @@ private NavigationExpansionExpressionState ReplaceNavigationExpansionExpressionS newPendingOrderings, newPendingIncludeChain, State.PendingCardinalityReducingOperator, - State.PendingTags, State.CustomRootMappings, State.MaterializeCollectionNavigation); } diff --git a/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpressionState.cs b/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpressionState.cs index 73d70c29afc..ff83239b93b 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpressionState.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationExpansionExpressionState.cs @@ -18,7 +18,6 @@ public NavigationExpansionExpressionState( List<(MethodInfo method, LambdaExpression keySelector)> pendingOrderings, NavigationBindingExpression pendingIncludeChain, MethodInfo pendingCardinalityReducingOperator, - List pendingTags, List> customRootMappings, INavigation materializeCollectionNavigation) { @@ -29,7 +28,6 @@ public NavigationExpansionExpressionState( PendingOrderings = pendingOrderings; PendingIncludeChain = pendingIncludeChain; PendingCardinalityReducingOperator = pendingCardinalityReducingOperator; - PendingTags = pendingTags; CustomRootMappings = customRootMappings; MaterializeCollectionNavigation = materializeCollectionNavigation; } @@ -41,7 +39,6 @@ public NavigationExpansionExpressionState( public virtual List<(MethodInfo method, LambdaExpression keySelector)> PendingOrderings { get; set; } public virtual NavigationBindingExpression PendingIncludeChain { get; set; } public virtual MethodInfo PendingCardinalityReducingOperator { get; set; } - public virtual List PendingTags { get; set; } public virtual List> CustomRootMappings { get; set; } public virtual INavigation MaterializeCollectionNavigation { get; set; } } diff --git a/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs b/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs index fdb03ca2d5e..1e830dd8df1 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs @@ -48,7 +48,6 @@ public static NavigationExpansionExpression CreateNavigationExpansionRoot( new List<(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, - pendingTags: new List(), customRootMappings: new List>(), materializeCollectionNavigation), materializeCollectionNavigation?.ClrType ?? operand.Type); diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs index a8f875c68dd..d330aa239d6 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs @@ -74,7 +74,6 @@ private Expression ProcessMemberPushdown( navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(newPendingSelector.Type), - navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); @@ -160,7 +159,6 @@ private Expression ProcessMemberPushdown( navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(combinedKeySelectorBody.Type), - navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, materializeCollectionNavigation: null); @@ -200,7 +198,6 @@ private Expression ProcessMemberPushdown( navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(boundSelectorBody.Type), - navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); @@ -364,7 +361,6 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator?.GetGenericMethodDefinition().MakeGenericMethod(navigationKeyAccessExpression.Type), - navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs index bd580abbd18..5bcd6b1e706 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs @@ -97,9 +97,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp case "ThenInclude": return ProcessInclude(methodCallExpression); - case nameof(EntityFrameworkQueryableExtensions.TagWith): - return ProcessWithTag(methodCallExpression); - default: return ProcessUnknownMethod(methodCallExpression); } @@ -164,7 +161,6 @@ private NavigationExpansionExpression VisitSourceExpression(Expression sourceExp new List<(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, - pendingTags: new List(), new List> { customRootMapping }, materializeCollectionNavigation: null); @@ -411,12 +407,9 @@ private NavigationExpansionExpressionState CreateSelectManyInnerState(Navigation pendingOrderings: new List<(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, - pendingTags: collectionSelectorState.PendingTags.ToList(), customRootMappings: customRootMappingMapping.Values.ToList(), materializeCollectionNavigation: null); - collectionSelectorState.PendingTags.Clear(); - return groupingState; } @@ -634,7 +627,6 @@ private Expression ProcessGroupJoin(MethodCallExpression methodCallExpression) outerApplyOrderingsResult.state.PendingOrderings, outerApplyOrderingsResult.state.PendingIncludeChain, outerApplyOrderingsResult.state.PendingCardinalityReducingOperator, - outerApplyOrderingsResult.state.PendingTags, outerApplyOrderingsResult.state.CustomRootMappings.Concat(new[] { groupingMapping }).ToList(), materializeCollectionNavigation: null); @@ -918,7 +910,6 @@ private Expression ProcessSkipTake(MethodCallExpression methodCallExpression) new List<(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, - pendingTags: new List(), new List> { customRootMapping }, materializeCollectionNavigation: null); @@ -1252,15 +1243,6 @@ private Expression ProcessFromSql(MethodCallExpression methodCallExpression) return new NavigationExpansionExpression(methodCallExpression, source.State, methodCallExpression.Type); } - private Expression ProcessWithTag(MethodCallExpression methodCallExpression) - { - var source = VisitSourceExpression(methodCallExpression.Arguments[0]); - var tag = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; - source.State.PendingTags.Add(tag); - - return source; - } - protected override Expression VisitConstant(ConstantExpression constantExpression) { if (constantExpression.Value != null @@ -1353,7 +1335,6 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio state.PendingOrderings, state.PendingIncludeChain, state.PendingCardinalityReducingOperator, - state.PendingTags, state.CustomRootMappings, state.MaterializeCollectionNavigation); @@ -1431,7 +1412,6 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio new List<(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, - outerState.PendingTags.Concat(innerState.PendingTags).ToList(), outerState.CustomRootMappings.Concat(innerState.CustomRootMappings).ToList(), materializeCollectionNavigation: null); diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs index 0026f24c2f0..828d4ccc9c9 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs @@ -36,7 +36,6 @@ protected override Expression VisitExtension(Expression extensionExpression) if (!state.ApplyPendingSelector && state.PendingOrderings.Count == 0 - && state.PendingTags.Count == 0 && state.PendingCardinalityReducingOperator == null && state.MaterializeCollectionNavigation == null) { @@ -67,15 +66,6 @@ protected override Expression VisitExtension(Expression extensionExpression) parameter = Expression.Parameter(result.Type.GetSequenceType()); } - if (state.PendingTags.Count > 0) - { - var withTagMethodInfo = EntityFrameworkQueryableExtensions.TagWithMethodInfo.MakeGenericMethod(parameter.Type); - foreach (var pendingTag in state.PendingTags) - { - result = Expression.Call(withTagMethodInfo, result, Expression.Constant(pendingTag)); - } - } - if (state.PendingCardinalityReducingOperator != null) { result = Expression.Call(state.PendingCardinalityReducingOperator, result); @@ -152,7 +142,6 @@ protected override Expression VisitExtension(Expression extensionExpression) navigationExpansionExpression.State.PendingOrderings, navigationExpansionExpression.State.PendingIncludeChain, navigationExpansionExpression.State.PendingCardinalityReducingOperator, - navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); diff --git a/src/EFCore/Query/Pipeline/QueryCompilationContext.cs b/src/EFCore/Query/Pipeline/QueryCompilationContext.cs index 067076c4481..0d2f34eb960 100644 --- a/src/EFCore/Query/Pipeline/QueryCompilationContext.cs +++ b/src/EFCore/Query/Pipeline/QueryCompilationContext.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -48,9 +49,15 @@ public QueryCompilationContext( public IModel Model { get; } public IDbContextOptions ContextOptions { get; } public bool TrackQueryResults { get; internal set; } + public ISet Tags { get; } = new HashSet(); public virtual IDiagnosticsLogger Logger { get; } public virtual Type ContextType { get; } + public virtual void AddTag(string tag) + { + Tags.Add(tag); + } + public virtual Func CreateQueryExecutor(Expression query) { query = _queryOptimizerFactory.Create(this).Visit(query); diff --git a/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs index 56210bc880c..94833f8b633 100644 --- a/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs @@ -20,6 +20,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp if (method.DeclaringType == typeof(EntityFrameworkQueryableExtensions) && method.IsGenericMethod) { + // We visit innerQueryable first so that we can get information in the same order operators are applied. var genericMethodDefinition = method.GetGenericMethodDefinition(); if (genericMethodDefinition == EntityFrameworkQueryableExtensions.AsTrackingMethodInfo || genericMethodDefinition == EntityFrameworkQueryableExtensions.AsNoTrackingMethodInfo) @@ -30,6 +31,14 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return innerQueryable; } + + if (genericMethodDefinition == EntityFrameworkQueryableExtensions.TagWithMethodInfo) + { + var innerQueryable = Visit(methodCallExpression.Arguments[0]); + _queryCompilationContext.AddTag((string)((ConstantExpression)methodCallExpression.Arguments[1]).Value); + + return innerQueryable; + } } return base.VisitMethodCall(methodCallExpression); diff --git a/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs index df95a96b8fa..ee245275c6a 100644 --- a/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs @@ -19,13 +19,13 @@ public QueryOptimizer(QueryCompilationContext queryCompilationContext) public Expression Visit(Expression query) { + query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query); query = new AllAnyToContainsRewritingExpressionVisitor().Visit(query); query = new GroupJoinFlatteningExpressionVisitor().Visit(query); query = new NullCheckRemovingExpressionVisitor().Visit(query); query = new EntityEqualityRewritingExpressionVisitor(_queryCompilationContext).Rewrite(query); query = new NavigationExpander(_queryCompilationContext.Model).ExpandNavigations(query); query = new EnumerableToQueryableReMappingExpressionVisitor().Visit(query); - query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query); query = new NullCheckRemovingExpressionVisitor().Visit(query); query = new FunctionPreprocessingVisitor().Visit(query); new EnumerableVerifyingExpressionVisitor().Visit(query); diff --git a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs index 2e6e25e2019..9a71327ce19 100644 --- a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs @@ -34,14 +34,16 @@ private static readonly PropertyInfo _cancellationTokenMemberInfo private readonly Expression _cancellationTokenParameter; private readonly EntityMaterializerInjectingExpressionVisitor _entityMaterializerInjectingExpressionVisitor; - public ShapedQueryCompilingExpressionVisitor(IEntityMaterializerSource entityMaterializerSource, bool trackQueryResults, bool async) + public ShapedQueryCompilingExpressionVisitor( + QueryCompilationContext queryCompilationContext, + IEntityMaterializerSource entityMaterializerSource) { _entityMaterializerSource = entityMaterializerSource; - TrackQueryResults = trackQueryResults; + TrackQueryResults = queryCompilationContext.TrackQueryResults; _entityMaterializerInjectingExpressionVisitor = - new EntityMaterializerInjectingExpressionVisitor(entityMaterializerSource, trackQueryResults); - Async = async; - if (async) + new EntityMaterializerInjectingExpressionVisitor(entityMaterializerSource, TrackQueryResults); + Async = queryCompilationContext.Async; + if (Async) { _cancellationTokenParameter = Expression.MakeMemberAccess( QueryCompilationContext.QueryContextParameter, diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryTaggingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryTaggingSqlServerTest.cs index c16d1f58891..65068922c7a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryTaggingSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryTaggingSqlServerTest.cs @@ -6,7 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - internal class QueryTaggingSqlServerTest : QueryTaggingTestBase> + public class QueryTaggingSqlServerTest : QueryTaggingTestBase> { public QueryTaggingSqlServerTest( NorthwindQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -51,14 +51,17 @@ public override void Tags_on_subquery() -- Laurel -SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] = N'ALFKI' +) AS [t] CROSS JOIN ( - SELECT TOP(5) [o].* + SELECT TOP(5) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ORDER BY [o].[OrderID] -) AS [t] -WHERE [c].[CustomerID] = N'ALFKI'"); +) AS [t0]"); } public override void Duplicate_tags() @@ -80,20 +83,14 @@ public override void Tag_on_include_query() AssertSql( @"-- Yanni -SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"-- Yanni - -SELECT [c.Orders].[OrderID], [c.Orders].[CustomerID], [c.Orders].[EmployeeID], [c.Orders].[OrderDate] -FROM [Orders] AS [c.Orders] -INNER JOIN ( - SELECT TOP(1) [c0].[CustomerID] - FROM [Customers] AS [c0] - ORDER BY [c0].[CustomerID] -) AS [t] ON [c.Orders].[CustomerID] = [t].[CustomerID] -ORDER BY [t].[CustomerID]"); +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID], [o].[OrderID]"); } public override void Tag_on_scalar_query() diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs index b7aa80e203b..e502a3fe32c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs @@ -24,7 +24,6 @@ public class SqlServerComplianceTest : RelationalComplianceTestBase typeof(InheritanceRelationshipsQueryTestBase<>), typeof(QueryNavigationsTestBase<>), typeof(ConcurrencyDetectorRelationalTestBase<>), - typeof(QueryTaggingTestBase<>), typeof(GearsOfWarFromSqlQueryTestBase<>), typeof(QueryNoClientEvalTestBase<>), typeof(WarningsTestBase<>),