Skip to content

Commit 3e07fd2

Browse files
committed
Query: Introduce client projections in SelectExpression
- ClientProjections is alternate when ProjectionMember binding is not possible using indexes - ClientProjections is separate from SelectExpression.Projection where former holds complex client side mapped projections and later holds only scalars - What it enables - Single result subquery is not joined right away - pendingCollections are removed - Ability to bind with above subquery translations after projection has converted to index based binding (enabling pending selector future change) - Ability to bind client projections smoothly like ProjectionMember binding so suppose translations post-client eval where it is supported - Grouping.FirstOrDefault can add subquery projection which applies separately so we can combine aggregate/non-aggregate projection on grouping - Introduce CollectionResultExpression which holds info on how to create collection for a subquery - ApplyProjection converts projectionMember/Index based bindings to actual scalar projection and updates shaper in same pass - Unique collectionId are assigned by shaper - Change in flow to projection to let sqlTranslator determines translatibility before processing for client side - Regression in collection over single result subquery - Earlier we applied single result right away copying over pending collection to outer which allowed single result subquery to convert apply to join - Now all client projections apply at same time so pending collection (equivalent subquery) is applied inside single result subquery so we fail to convert apply to join Ground work for #20291, #12088, #13805 Resolves #23571
1 parent 277b43a commit 3e07fd2

File tree

37 files changed

+1903
-1568
lines changed

37 files changed

+1903
-1568
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq.Expressions;
6+
using Microsoft.EntityFrameworkCore.Infrastructure;
7+
using Microsoft.EntityFrameworkCore.Metadata;
8+
using Microsoft.EntityFrameworkCore.Utilities;
9+
10+
namespace Microsoft.EntityFrameworkCore.Query
11+
{
12+
/// <summary>
13+
/// <para>
14+
/// An expression that represents creation of a collection in <see cref="ShapedQueryExpression.ShaperExpression" /> for relational providers.
15+
/// </para>
16+
/// <para>
17+
/// This type is typically used by database providers (and other extensions). It is generally
18+
/// not used in application code.
19+
/// </para>
20+
/// </summary>
21+
public class CollectionResultExpression : Expression, IPrintableExpression
22+
{
23+
/// <summary>
24+
/// Creates a new instance of the <see cref="CollectionResultExpression" /> class.
25+
/// </summary>
26+
/// <param name="projectionBindingExpression"> An expression reprensenting how to get the subquery from SelectExpression to get the elements. </param>
27+
/// <param name="navigation"> A navigation associated with this collection, if any. </param>
28+
/// <param name="elementType"> The clr type of individual elements in the collection. </param>
29+
public CollectionResultExpression(
30+
ProjectionBindingExpression projectionBindingExpression,
31+
INavigationBase? navigation,
32+
Type elementType)
33+
{
34+
ProjectionBindingExpression = projectionBindingExpression;
35+
Navigation = navigation;
36+
ElementType = elementType;
37+
}
38+
39+
/// <summary>
40+
/// The expression to get the subquery for this collection.
41+
/// </summary>
42+
public virtual ProjectionBindingExpression ProjectionBindingExpression { get; }
43+
/// <summary>
44+
/// The navigation if associated with the collection.
45+
/// </summary>
46+
public virtual INavigationBase? Navigation { get; }
47+
/// <summary>
48+
/// The clr type of elements of the collection.
49+
/// </summary>
50+
public virtual Type ElementType { get; }
51+
52+
/// <inheritdoc />
53+
public override Type Type => ProjectionBindingExpression.Type;
54+
/// <inheritdoc />
55+
public override ExpressionType NodeType => ExpressionType.Extension;
56+
57+
/// <inheritdoc />
58+
protected override Expression VisitChildren(ExpressionVisitor visitor)
59+
{
60+
Check.NotNull(visitor, nameof(visitor));
61+
62+
var projectionBindingExpression = (ProjectionBindingExpression)visitor.Visit(ProjectionBindingExpression);
63+
64+
return Update(projectionBindingExpression);
65+
}
66+
67+
/// <summary>
68+
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will
69+
/// return this expression.
70+
/// </summary>
71+
/// <param name="projectionBindingExpression"> The <see cref="ProjectionBindingExpression" /> property of the result. </param>
72+
/// <returns> This expression if no children changed, or an expression with the updated children. </returns>
73+
public virtual CollectionResultExpression Update(ProjectionBindingExpression projectionBindingExpression)
74+
{
75+
Check.NotNull(projectionBindingExpression, nameof(projectionBindingExpression));
76+
77+
return projectionBindingExpression != ProjectionBindingExpression
78+
? new CollectionResultExpression(projectionBindingExpression, Navigation, ElementType)
79+
: this;
80+
}
81+
82+
/// <inheritdoc />
83+
public virtual void Print(ExpressionPrinter expressionPrinter)
84+
{
85+
expressionPrinter.AppendLine("CollectionResultExpression:");
86+
using (expressionPrinter.Indent())
87+
{
88+
expressionPrinter.Append("ProjectionBindingExpression:");
89+
expressionPrinter.Visit(ProjectionBindingExpression);
90+
expressionPrinter.AppendLine();
91+
if (Navigation != null)
92+
{
93+
expressionPrinter.Append("Navigation:").AppendLine(Navigation.ToString()!);
94+
}
95+
expressionPrinter.Append("ElementType:").AppendLine(ElementType.ShortDisplayName());
96+
}
97+
}
98+
}
99+
}

src/EFCore.Relational/Query/EntityProjectionExpression.cs

+3
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,8 @@ public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExp
216216
? expression
217217
: null;
218218
}
219+
220+
/// <inheritdoc />
221+
public override string ToString() => $"EntityProjectionExpression: {EntityType.ShortName()}";
219222
}
220223
}

src/EFCore.Relational/Query/Internal/CollectionJoinApplyingExpressionVisitor.cs

-99
This file was deleted.

0 commit comments

Comments
 (0)