Skip to content

Commit

Permalink
Fix to #16231 - Query: Include on client projection gets ignored
Browse files Browse the repository at this point in the history
Problem was that when we search and apply include on the final projection we short circuit when we encounter member access. However, we should only do that if the member access is on top of navigation binding - in other cases, like client-side functions, we should peek inside and potentially apply includes on entities that are defined there.
  • Loading branch information
maumar committed Jun 26, 2019
1 parent d742250 commit b86f393
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@ namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors
{
public class PendingSelectorIncludeRewriter : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression memberExpression) => memberExpression;
protected override Expression VisitMember(MemberExpression memberExpression)
{
if (memberExpression.Expression is NavigationBindingExpression)
{
return memberExpression;
}

var newExpression = Visit(memberExpression.Expression);

return newExpression != memberExpression.Expression
? Expression.MakeMemberAccess(newExpression, memberExpression.Member)
: memberExpression;
}

protected override Expression VisitInvocation(InvocationExpression invocationExpression) => invocationExpression;
protected override Expression VisitLambda<T>(Expression<T> lambdaExpression) => lambdaExpression;
protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) => typeBinaryExpression;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ public class PendingIncludeFindingVisitor : ExpressionVisitor
{
public virtual Dictionary<NavigationTreeNode, SourceMapping> PendingIncludes { get; } = new Dictionary<NavigationTreeNode, SourceMapping>();

protected override Expression VisitMember(MemberExpression memberExpression) => memberExpression;
protected override Expression VisitMember(MemberExpression memberExpression)
{
if (memberExpression.Expression is NavigationBindingExpression)
{
return memberExpression;
}

Visit(memberExpression.Expression);

return memberExpression;
}

protected override Expression VisitInvocation(InvocationExpression invocationExpression) => invocationExpression;
protected override Expression VisitLambda<T>(Expression<T> lambdaExpression) => lambdaExpression;
protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) => typeBinaryExpression;
Expand Down
13 changes: 13 additions & 0 deletions test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7182,6 +7182,19 @@ public virtual Task Include_collection_with_Cast_to_base(bool isAsync)
new List<IExpectedInclude> { new ExpectedInclude<Gear>(e => e.Weapons, "Weapons") });
}

[ConditionalFact]
public virtual void Include_with_client_method_and_member_access_still_applies_includes()
{
using (var ctx = CreateContext())
{
var query = ctx.Gears
.Include(g => g.Tag)
.Select(g => new { g.Nickname, Client(g).FullName });

var result = query.ToList();
}
}

[ConditionalFact(Skip = "Issue #15611")]
public virtual Task Multiple_includes_with_client_method_around_qsre_and_also_projecting_included_collection()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8208,6 +8208,17 @@ FROM [Gears] AS [g0]
ORDER BY [t].[FullName]");
}

public override void Include_with_client_method_and_member_access_still_applies_includes()
{
base.Include_with_client_method_and_member_access_still_applies_includes();

AssertSql(
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note]
FROM [Gears] AS [g]
LEFT JOIN [Tags] AS [t] ON (([g].[Nickname] = [t].[GearNickName]) AND [t].[GearNickName] IS NOT NULL) AND (([g].[SquadId] = [t].[GearSquadId]) AND [t].[GearSquadId] IS NOT NULL)
WHERE [g].[Discriminator] IN (N'Gear', N'Officer')");
}

public override async Task Multiple_includes_with_client_method_around_qsre_and_also_projecting_included_collection()
{
await base.Multiple_includes_with_client_method_around_qsre_and_also_projecting_included_collection();
Expand Down

0 comments on commit b86f393

Please sign in to comment.