From 5c1f8e99d02d737fd6dc41b7d8dee5975ddf8587 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Tue, 9 Apr 2019 09:48:50 -0700 Subject: [PATCH] stub --- ...yableMethodTranslatingExpressionVisitor.cs | 39 +++++++++++++++ .../Query/PipeLine/EntityShaperExpression.cs | 42 ++++++++++++++++ ...yableMethodTranslatingExpressionVisitor.cs | 49 +++++++++++++++++++ .../PipeLine/ShapedQueryExpressionVisitor.cs | 23 +++++++++ 4 files changed, 153 insertions(+) diff --git a/src/EFCore.Relational/Query/PipeLine/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/PipeLine/RelationalQueryableMethodTranslatingExpressionVisitor.cs index b3d1051121b..198201fa759 100644 --- a/src/EFCore.Relational/Query/PipeLine/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/PipeLine/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -262,6 +262,45 @@ protected override ShapedQueryExpression TranslateFirstOrDefault(ShapedQueryExpr protected override ShapedQueryExpression TranslateGroupJoin(ShapedQueryExpression outer, ShapedQueryExpression inner, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) { + var outerSelectExpression = (SelectExpression)outer.QueryExpression; + if (outerSelectExpression.Limit != null + || outerSelectExpression.Offset != null + || outerSelectExpression.IsDistinct) + { + outerSelectExpression.PushdownIntoSubQuery(); + } + + var innerSelectExpression = (SelectExpression)inner.QueryExpression; + if (innerSelectExpression.Orderings.Any() + || innerSelectExpression.Limit != null + || innerSelectExpression.Offset != null + || innerSelectExpression.IsDistinct + || innerSelectExpression.Predicate != null) + { + innerSelectExpression.PushdownIntoSubQuery(); + } + + var joinPredicate = CreateJoinPredicate(outer, outerKeySelector, inner, innerKeySelector); + if (joinPredicate != null) + { + outer = TranslateThenBy(outer, outerKeySelector, true); + + var innerTransparentIdentifierType = CreateTransparentIdentifierType( + resultSelector.Parameters[0].Type, + resultSelector.Parameters[1].Type.TryGetSequenceType()); + + outerSelectExpression.AddLeftJoin( + innerSelectExpression, joinPredicate, innerTransparentIdentifierType); + + return TranslateResultSelectorForGroupJoin( + outer, + inner.ShaperExpression, + outerKeySelector, + innerKeySelector, + resultSelector, + innerTransparentIdentifierType); + } + throw new NotImplementedException(); } diff --git a/src/EFCore/Query/PipeLine/EntityShaperExpression.cs b/src/EFCore/Query/PipeLine/EntityShaperExpression.cs index b92cde9e4f5..4a442ea0ab0 100644 --- a/src/EFCore/Query/PipeLine/EntityShaperExpression.cs +++ b/src/EFCore/Query/PipeLine/EntityShaperExpression.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.Metadata; @@ -37,4 +38,45 @@ public EntityShaperExpression Update(ProjectionBindingExpression valueBufferExpr public override Type Type => EntityType.ClrType; public override ExpressionType NodeType => ExpressionType.Extension; } + + public class CollectionShaperExpression : Expression + { + public CollectionShaperExpression( + Expression parent, + Expression innerShaper, + Expression outerKey, + Expression innerKey) + { + Parent = parent; + InnerShaper = innerShaper; + OuterKey = outerKey; + InnerKey = innerKey; + } + + + protected override Expression VisitChildren(ExpressionVisitor visitor) + { + var parent = visitor.Visit(Parent); + var innerShaper = visitor.Visit(InnerShaper); + var outerKey = visitor.Visit(OuterKey); + var innerKey = visitor.Visit(InnerKey); + + return parent != Parent || innerShaper != InnerShaper || outerKey != OuterKey || innerKey != InnerKey + ? new CollectionShaperExpression(parent, innerShaper, outerKey, innerKey) + : this; + } + + + public override ExpressionType NodeType => ExpressionType.Extension; + + public override Type Type => typeof(IEnumerable<>).MakeGenericType(InnerShaper.Type); + + public Expression Parent { get; } + + public Expression InnerShaper { get; } + + public Expression OuterKey { get; } + + public Expression InnerKey { get; } + } } diff --git a/src/EFCore/Query/PipeLine/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/PipeLine/QueryableMethodTranslatingExpressionVisitor.cs index 2630612a745..325a30806bb 100644 --- a/src/EFCore/Query/PipeLine/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/PipeLine/QueryableMethodTranslatingExpressionVisitor.cs @@ -486,6 +486,55 @@ protected ShapedQueryExpression TranslateResultSelectorForJoin( return TranslateSelect(outer, newResultSelector); } + protected ShapedQueryExpression TranslateResultSelectorForGroupJoin( + ShapedQueryExpression outer, + LambdaExpression innerShaper, + LambdaExpression outerKeySelector, + LambdaExpression innerKeySelector, + LambdaExpression resultSelector, + Type transparentIdentifierType) + { + innerShaper = Expression.Lambda( + new EntityShaperNullableMarkingExpressionVisitor().Visit(innerShaper.Body), + innerShaper.Parameters); + + var shaperExpression = CombineShapers( + outer.QueryExpression, + outer.ShaperExpression, + innerShaper, + transparentIdentifierType); + + var transparentIdentifierParameter = Expression.Parameter(transparentIdentifierType); + var outerAccess = AccessOuterTransparentField(transparentIdentifierType, transparentIdentifierParameter); + var innerAccess = AccessInnerTransparentField(transparentIdentifierType, transparentIdentifierParameter); + + var outerKey = ReplacingExpressionVisitor.Replace( + outerKeySelector.Parameters[0], + outerAccess, + outerKeySelector.Body); + + var innerKey = ReplacingExpressionVisitor.Replace( + innerKeySelector.Parameters[0], + innerAccess, + innerKeySelector.Body); + + var replacements = new Dictionary + { + { resultSelector.Parameters[0], outerAccess }, + { resultSelector.Parameters[1], new CollectionShaperExpression(outerAccess, innerAccess, outerKey, innerKey) }, + }; + + var resultBody = new ReplacingExpressionVisitor(replacements).Visit(resultSelector.Body); + resultBody = ReplacingExpressionVisitor.Replace( + transparentIdentifierParameter, + shaperExpression.Body, + resultBody); + + outer.ShaperExpression = Expression.Lambda(resultBody, shaperExpression.Parameters); + + return outer; + } + private LambdaExpression CombineShapers( Expression queryExpression, LambdaExpression outerShaper, diff --git a/src/EFCore/Query/PipeLine/ShapedQueryExpressionVisitor.cs b/src/EFCore/Query/PipeLine/ShapedQueryExpressionVisitor.cs index d2f55ba01d9..01eaad2ee60 100644 --- a/src/EFCore/Query/PipeLine/ShapedQueryExpressionVisitor.cs +++ b/src/EFCore/Query/PipeLine/ShapedQueryExpressionVisitor.cs @@ -83,6 +83,9 @@ protected virtual LambdaExpression InjectEntityMaterializer( private class EntityMaterializerInjectingExpressionVisitor : ExpressionVisitor { + private IDictionary _entityCache + = new Dictionary(); + private static readonly ConstructorInfo _materializationContextConstructor = typeof(MaterializationContext).GetConstructors().Single(ci => ci.GetParameters().Length == 2); @@ -129,6 +132,11 @@ protected override Expression VisitExtension(Expression extensionExpression) { if (extensionExpression is EntityShaperExpression entityShaperExpression) { + if (_entityCache.TryGetValue(entityShaperExpression, out var existingInstance)) + { + return existingInstance; + } + _currentEntityIndex++; var entityType = entityShaperExpression.EntityType; var valueBuffer = entityShaperExpression.ValueBufferExpression; @@ -204,9 +212,24 @@ protected override Expression VisitExtension(Expression extensionExpression) MaterializeEntity(entityType, valueBuffer)))); } + _entityCache[entityShaperExpression] = result; + return result; } + if (extensionExpression is CollectionShaperExpression collectionShaper) + { + var keyType = collectionShaper.OuterKey.Type; + var comparerType = typeof(EqualityComparer<>).MakeGenericType(keyType); + var comparer = Expression.Variable(comparerType, "comparer" + _currentEntityIndex); + + _variables.Add(comparer); + Expression.Assign( + comparer, + Expression.MakeMemberAccess(null, comparerType.GetProperty(nameof(EqualityComparer.Default)))); + var parent = Visit(collectionShaper.Parent); + } + if (extensionExpression is ProjectionBindingExpression) { return extensionExpression;