From 1380949cfe6c69815c7be95e71ba415afa491035 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Sat, 27 Jun 2015 22:06:16 +0200 Subject: [PATCH 1/2] #2666 Update Remotion.Linq to v2.0.0-beta.2 * Updated dependency. * Updated API changes: ** Injected stub-implementation for EvaluatableExpressionFilterBase into PartialEvaluatingExpressionTreeProcessor. This can later be changed to exclude specific method/property calls (e.g. DateTime.Now) from partial evaluation. ** ReferenceReplacingExpressionVisitor is no longer available as extension point. Usages have been replaced with equivalent semantics. ** FromClause no longer exposes settable properties. The QueryOptimizer has been changed to use a stub object for transferring the values during sub-query flattening. --- .../Query/CompiledQueryCache.cs | 13 +++-- .../MemberAccessBindingExpressionVisitor.cs | 22 +++++---- .../ParameterExtractingExpressionVisitor.cs | 5 +- .../Query/QueryOptimizer.cs | 47 ++++++++++++++++--- .../ThenIncludeExpressionNode.cs | 4 +- src/EntityFramework7.Core/project.json | 2 +- 6 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/EntityFramework7.Core/Query/CompiledQueryCache.cs b/src/EntityFramework7.Core/Query/CompiledQueryCache.cs index 20857bfbef1..87d2dc8b3fe 100644 --- a/src/EntityFramework7.Core/Query/CompiledQueryCache.cs +++ b/src/EntityFramework7.Core/Query/CompiledQueryCache.cs @@ -20,6 +20,7 @@ using Remotion.Linq.Clauses.Expressions; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; +using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation; using Remotion.Linq.Parsing.Structure; using Remotion.Linq.Parsing.Structure.ExpressionTreeProcessors; using Remotion.Linq.Parsing.Structure.NodeTypeProviders; @@ -160,7 +161,7 @@ private CompiledQuery GetOrAdd( var parameterizedQuery = ParameterExtractingExpressionVisitor - .ExtractParameters(query, queryContext); + .ExtractParameters(query, queryContext, new NullEvaluatableExpressionFilter()); var cacheKey = database.Model.GetHashCode().ToString() @@ -204,11 +205,15 @@ private static QueryParser CreateQueryParser() _cachedNodeTypeProvider.Value, new CompoundExpressionTreeProcessor(new IExpressionTreeProcessor[] { - new PartialEvaluatingExpressionTreeProcessor(), + new PartialEvaluatingExpressionTreeProcessor(new NullEvaluatableExpressionFilter()), new FunctionEvaluationEnablingProcessor(), new TransformingExpressionTreeProcessor(ExpressionTransformerRegistry.CreateDefault()) }))); + private class NullEvaluatableExpressionFilter : EvaluatableExpressionFilterBase + { + } + private class FunctionEvaluationEnablingProcessor : IExpressionTreeProcessor { public Expression Process(Expression expressionTree) @@ -247,7 +252,7 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) private static ReadonlyNodeTypeProvider CreateNodeTypeProvider() { - var methodInfoBasedNodeTypeRegistry = MethodInfoBasedNodeTypeRegistry.CreateFromRemotionLinqAssembly(); + var methodInfoBasedNodeTypeRegistry = MethodInfoBasedNodeTypeRegistry.CreateFromRelinqAssembly(); methodInfoBasedNodeTypeRegistry .Register(QueryAnnotationExpressionNode.SupportedMethods, typeof(QueryAnnotationExpressionNode)); @@ -262,7 +267,7 @@ var innerProviders = new INodeTypeProvider[] { methodInfoBasedNodeTypeRegistry, - MethodNameBasedNodeTypeRegistry.CreateFromRemotionLinqAssembly() + MethodNameBasedNodeTypeRegistry.CreateFromRelinqAssembly() }; return new ReadonlyNodeTypeProvider(new CompoundNodeTypeProvider(innerProviders)); diff --git a/src/EntityFramework7.Core/Query/ExpressionVisitors/MemberAccessBindingExpressionVisitor.cs b/src/EntityFramework7.Core/Query/ExpressionVisitors/MemberAccessBindingExpressionVisitor.cs index f724af5382f..b52068baf4f 100644 --- a/src/EntityFramework7.Core/Query/ExpressionVisitors/MemberAccessBindingExpressionVisitor.cs +++ b/src/EntityFramework7.Core/Query/ExpressionVisitors/MemberAccessBindingExpressionVisitor.cs @@ -11,12 +11,13 @@ using Microsoft.Data.Entity.Utilities; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; -using Remotion.Linq.Clauses.ExpressionVisitors; +using Remotion.Linq.Parsing; namespace Microsoft.Data.Entity.Query.ExpressionVisitors { - public class MemberAccessBindingExpressionVisitor : ReferenceReplacingExpressionVisitor + public class MemberAccessBindingExpressionVisitor : RelinqExpressionVisitor { + private readonly QuerySourceMapping _querySourceMapping; private readonly EntityQueryModelVisitor _queryModelVisitor; private readonly bool _inProjection; @@ -24,12 +25,11 @@ public MemberAccessBindingExpressionVisitor( [NotNull] QuerySourceMapping querySourceMapping, [NotNull] EntityQueryModelVisitor queryModelVisitor, bool inProjection) - : base( - Check.NotNull(querySourceMapping, nameof(querySourceMapping)), - throwOnUnmappedReferences: false) { + Check.NotNull(querySourceMapping, nameof(querySourceMapping)); Check.NotNull(queryModelVisitor, nameof(queryModelVisitor)); + _querySourceMapping = querySourceMapping; _queryModelVisitor = queryModelVisitor; _inProjection = inProjection; } @@ -81,9 +81,9 @@ protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpr Check.NotNull(querySourceReferenceExpression, nameof(querySourceReferenceExpression)); var newExpression - = QuerySourceMapping.ContainsMapping(querySourceReferenceExpression.ReferencedQuerySource) - ? QuerySourceMapping.GetExpression(querySourceReferenceExpression.ReferencedQuerySource) - : base.VisitQuerySourceReference(querySourceReferenceExpression); + = _querySourceMapping.ContainsMapping(querySourceReferenceExpression.ReferencedQuerySource) + ? _querySourceMapping.GetExpression(querySourceReferenceExpression.ReferencedQuerySource) + : querySourceReferenceExpression; if (_inProjection && newExpression.Type.IsConstructedGenericType) @@ -111,6 +111,12 @@ var newExpression return newExpression; } + protected override Expression VisitSubQuery(SubQueryExpression expression) + { + expression.QueryModel.TransformExpressions(Visit); + return expression; + } + protected override Expression VisitMember(MemberExpression memberExpression) { Check.NotNull(memberExpression, nameof(memberExpression)); diff --git a/src/EntityFramework7.Core/Query/ExpressionVisitors/ParameterExtractingExpressionVisitor.cs b/src/EntityFramework7.Core/Query/ExpressionVisitors/ParameterExtractingExpressionVisitor.cs index dbd1464b9c0..4d68777afc8 100644 --- a/src/EntityFramework7.Core/Query/ExpressionVisitors/ParameterExtractingExpressionVisitor.cs +++ b/src/EntityFramework7.Core/Query/ExpressionVisitors/ParameterExtractingExpressionVisitor.cs @@ -15,10 +15,11 @@ public class ParameterExtractingExpressionVisitor : ExpressionVisitorBase { public static Expression ExtractParameters( [NotNull] Expression expressionTree, - [NotNull] QueryContext queryContext) + [NotNull] QueryContext queryContext, + [NotNull] IEvaluatableExpressionFilter evaluatableExpressionFilter) { var functionEvaluationDisabledExpression = new FunctionEvaluationDisablingVisitor().Visit(expressionTree); - var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor.Analyze(functionEvaluationDisabledExpression); + var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor.Analyze(functionEvaluationDisabledExpression, evaluatableExpressionFilter); var visitor = new ParameterExtractingExpressionVisitor(partialEvaluationInfo, queryContext); return visitor.Visit(functionEvaluationDisabledExpression); diff --git a/src/EntityFramework7.Core/Query/QueryOptimizer.cs b/src/EntityFramework7.Core/Query/QueryOptimizer.cs index a8ea9667514..78cb9e5c97a 100644 --- a/src/EntityFramework7.Core/Query/QueryOptimizer.cs +++ b/src/EntityFramework7.Core/Query/QueryOptimizer.cs @@ -1,8 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.Data.Entity.Query.Annotations; using Microsoft.Data.Entity.Utilities; @@ -17,6 +19,32 @@ namespace Microsoft.Data.Entity.Query { public class QueryOptimizer : SubQueryFromClauseFlattener { + private class FromClauseData : IFromClause + { + public string ItemName { get; } + + public Type ItemType { get; } + + public Expression FromExpression { get; } + + public FromClauseData(string itemName, Type itemType, Expression fromExpression) + { + ItemName = itemName; + ItemType = itemType; + FromExpression = fromExpression; + } + + void IClause.TransformExpressions(Func transformation) + { + throw new NotSupportedException(); + } + + void IFromClause.CopyFromSource(IFromClause source) + { + throw new NotSupportedException(); + } + } + private readonly IReadOnlyCollection _queryAnnotations; public QueryOptimizer([NotNull] IReadOnlyCollection queryAnnotations) @@ -57,7 +85,7 @@ in _queryAnnotations protected override void FlattenSubQuery( [NotNull] SubQueryExpression subQueryExpression, - [NotNull] FromClauseBase fromClause, + [NotNull] IFromClause fromClause, [NotNull] QueryModel queryModel, int destinationIndex) { @@ -75,18 +103,25 @@ protected override void FlattenSubQuery( || (queryModel.IsIdentityQuery() && !queryModel.ResultOperators.Any())) { + string itemName; var innerMainFromClause = subQueryExpression.QueryModel.MainFromClause; - var isGeneratedNameOuter = fromClause.HasGeneratedItemName(); - var outerItemName = fromClause.ItemName; - CopyFromClauseData(innerMainFromClause, fromClause); - if (innerMainFromClause.HasGeneratedItemName() && !isGeneratedNameOuter) { - fromClause.ItemName = outerItemName; + itemName = fromClause.ItemName; + } + else + { + itemName = innerMainFromClause.ItemName; } + var fromClauseData = new FromClauseData( + itemName, + innerMainFromClause.ItemType, + innerMainFromClause.FromExpression); + fromClause.CopyFromSource(fromClauseData); + var innerSelectorMapping = new QuerySourceMapping(); innerSelectorMapping.AddMapping(fromClause, subQueryExpression.QueryModel.SelectClause.Selector); diff --git a/src/EntityFramework7.Core/Query/ResultOperators/ThenIncludeExpressionNode.cs b/src/EntityFramework7.Core/Query/ResultOperators/ThenIncludeExpressionNode.cs index c7eaac14063..4b18e7668b4 100644 --- a/src/EntityFramework7.Core/Query/ResultOperators/ThenIncludeExpressionNode.cs +++ b/src/EntityFramework7.Core/Query/ResultOperators/ThenIncludeExpressionNode.cs @@ -32,7 +32,7 @@ public ThenIncludeExpressionNode( _navigationPropertyPathLambda = navigationPropertyPathLambda; } - protected override QueryModel ApplyNodeSpecificSemantics(QueryModel queryModel, ClauseGenerationContext clauseGenerationContext) + protected override void ApplyNodeSpecificSemantics(QueryModel queryModel, ClauseGenerationContext clauseGenerationContext) { var queryAnnotationResultOperator = (QueryAnnotationResultOperator)clauseGenerationContext.GetContextInfo(Source); @@ -41,8 +41,6 @@ var queryAnnotationResultOperator .AppendToNavigationPath(_navigationPropertyPathLambda.GetComplexPropertyAccess()); clauseGenerationContext.AddContextInfo(this, queryAnnotationResultOperator); - - return queryModel; } protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) => null; diff --git a/src/EntityFramework7.Core/project.json b/src/EntityFramework7.Core/project.json index 0a3d30b58ee..4fa45df416f 100644 --- a/src/EntityFramework7.Core/project.json +++ b/src/EntityFramework7.Core/project.json @@ -17,7 +17,7 @@ "Microsoft.Framework.Logging": "1.0.0-*", "Microsoft.Framework.Logging.Abstractions": "1.0.0-*", "Microsoft.Framework.OptionsModel": "1.0.0-*", - "Remotion.Linq": "2.0.0-alpha-004", + "Remotion.Linq": "2.0.0-beta-002", "System.Collections.Immutable": "1.1.37-*" }, "compile": "..\\Shared\\*.cs", From dd984e01336cb53ca3bd76db9b47a77855029eb8 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Sun, 19 Jul 2015 17:37:33 +0200 Subject: [PATCH 2/2] #2666 Make MethodCallEvaluationPreventingExpression and PropertyEvaluationPreventingExpression non-reducible to work with updated partial evaluation behavior. The MethodCallEvaluationPreventingExpression and PropertyEvaluationPreventingExpression are only used to circumvent partial evaluation. Making them non-reducible does not have side effects. --- .../MethodCallEvaluationPreventingExpression.cs | 10 ---------- .../PropertyEvaluationPreventingExpression.cs | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/src/EntityFramework7.Core/Query/Expressions/MethodCallEvaluationPreventingExpression.cs b/src/EntityFramework7.Core/Query/Expressions/MethodCallEvaluationPreventingExpression.cs index 79211c146c8..86342b56753 100644 --- a/src/EntityFramework7.Core/Query/Expressions/MethodCallEvaluationPreventingExpression.cs +++ b/src/EntityFramework7.Core/Query/Expressions/MethodCallEvaluationPreventingExpression.cs @@ -25,16 +25,6 @@ public MethodCallEvaluationPreventingExpression([NotNull] MethodCallExpression a public override Type Type => _methodCall.Type; - public override bool CanReduce - { - get { return true; } - } - - public override Expression Reduce() - { - return MethodCall; - } - protected override Expression VisitChildren(ExpressionVisitor visitor) { var newObject = visitor.Visit(MethodCall.Object); diff --git a/src/EntityFramework7.Core/Query/Expressions/PropertyEvaluationPreventingExpression.cs b/src/EntityFramework7.Core/Query/Expressions/PropertyEvaluationPreventingExpression.cs index 0029bab1d16..1c5c264359e 100644 --- a/src/EntityFramework7.Core/Query/Expressions/PropertyEvaluationPreventingExpression.cs +++ b/src/EntityFramework7.Core/Query/Expressions/PropertyEvaluationPreventingExpression.cs @@ -22,16 +22,6 @@ public PropertyEvaluationPreventingExpression([NotNull] MemberExpression argumen public override Type Type => _memberExpression.Type; - public override bool CanReduce - { - get { return true; } - } - - public override Expression Reduce() - { - return MemberExpression; - } - protected override Expression VisitChildren(ExpressionVisitor visitor) { var newExpression = visitor.Visit(MemberExpression.Expression);