Skip to content

Commit

Permalink
Query: Get mapped projection from associated query expression (#22254)
Browse files Browse the repository at this point in the history
Since projection binding can come from parent query expression too

Resolves #12239
  • Loading branch information
smitpatel committed Aug 27, 2020
1 parent a92d732 commit 8fff1f6
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,8 @@ protected override Expression VisitExtension(Expression extensionExpression)
EntityProjectionExpression entityProjectionExpression;
if (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression)
{
VerifyQueryExpression(projectionBindingExpression);
entityProjectionExpression = (EntityProjectionExpression)_queryExpression.GetMappedProjection(
projectionBindingExpression.ProjectionMember);
entityProjectionExpression = (EntityProjectionExpression)((InMemoryQueryExpression)projectionBindingExpression.QueryExpression)
.GetMappedProjection(projectionBindingExpression.ProjectionMember);
}
else
{
Expand Down Expand Up @@ -486,15 +485,6 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression)
: unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type));
}

// TODO: Debugging
private void VerifyQueryExpression(ProjectionBindingExpression projectionBindingExpression)
{
if (projectionBindingExpression.QueryExpression != _queryExpression)
{
throw new InvalidOperationException(CoreStrings.QueryFailed(projectionBindingExpression.Print(), GetType().Name));
}
}

private CollectionShaperExpression AddCollectionProjection(
ShapedQueryExpression subquery,
INavigationBase navigation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ public override Expression Visit(Expression expression)
if (projectionBindingExpression.ProjectionMember != null)
{
// This would be SqlExpression. EntityProjectionExpression would be wrapped inside EntityShaperExpression.
var mappedProjection = (SqlExpression)_selectExpression.GetMappedProjection(
projectionBindingExpression.ProjectionMember);
var mappedProjection = (SqlExpression)((SelectExpression)projectionBindingExpression.QueryExpression)
.GetMappedProjection(projectionBindingExpression.ProjectionMember);

return new ProjectionBindingExpression(
_selectExpression, _selectExpression.AddToProjection(mappedProjection), expression.Type);
Expand Down Expand Up @@ -299,7 +299,6 @@ protected override Expression VisitExtension(Expression extensionExpression)
EntityProjectionExpression entityProjectionExpression;
if (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression)
{
VerifySelectExpression(projectionBindingExpression);
// If projectionBinding is not mapped then SelectExpression has client projection
// Hence force client eval
if (projectionBindingExpression.ProjectionMember == null)
Expand All @@ -318,8 +317,8 @@ protected override Expression VisitExtension(Expression extensionExpression)
return null;
}

entityProjectionExpression = (EntityProjectionExpression)_selectExpression.GetMappedProjection(
projectionBindingExpression.ProjectionMember);
entityProjectionExpression = (EntityProjectionExpression)((SelectExpression)projectionBindingExpression.QueryExpression)
.GetMappedProjection(projectionBindingExpression.ProjectionMember);
}
else
{
Expand Down Expand Up @@ -570,15 +569,6 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression)
: unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type));
}

// TODO: Debugging
private void VerifySelectExpression(ProjectionBindingExpression projectionBindingExpression)
{
if (projectionBindingExpression.QueryExpression != _selectExpression)
{
throw new InvalidOperationException(CoreStrings.QueryFailed(projectionBindingExpression.Print(), GetType().Name));
}
}

private static Expression MatchTypes(Expression expression, Type targetType)
{
if (targetType != expression.Type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,5 +878,27 @@ private bool Equals(OrderDetailViewModel orderDetailViewModel)
public override int GetHashCode()
=> HashCode.Combine(_orderID, _productID);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task SelectMany_with_selecting_outer_entity(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>()
.SelectMany(c => c.Orders.Select(o => c)),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task SelectMany_with_selecting_outer_element(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>()
.Select(e => new { e, Complex = e.CustomerID + e.City })
.SelectMany(c => c.e.Orders.Select(o => c.Complex)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,34 @@ WHERE [c].[CustomerID] LIKE N'A%'
ORDER BY [c].[CustomerID], [t0].[OrderID0], [t0].[OrderID1], [t0].[ProductID0]");
}

public override async Task SelectMany_with_selecting_outer_entity(bool async)
{
await base.SelectMany_with_selecting_outer_entity(async);

AssertSql(
@"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region]
FROM [Customers] AS [c]
CROSS APPLY (
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Orders] AS [o]
WHERE [c].[CustomerID] = [o].[CustomerID]
) AS [t]");
}

public override async Task SelectMany_with_selecting_outer_element(bool async)
{
await base.SelectMany_with_selecting_outer_element(async);

AssertSql(
@"SELECT [t].[c]
FROM [Customers] AS [c]
CROSS APPLY (
SELECT [c].[CustomerID] + COALESCE([c].[City], N'') AS [c]
FROM [Orders] AS [o]
WHERE [c].[CustomerID] = [o].[CustomerID]
) AS [t]");
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,17 @@ public override async Task SelectMany_with_client_eval_with_collection_shaper_ig
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
() => base.SelectMany_with_client_eval_with_collection_shaper_ignored(async))).Message);

public override async Task SelectMany_with_selecting_outer_entity(bool async)
=> Assert.Equal(
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
() => base.SelectMany_with_selecting_outer_entity(async))).Message);

public override async Task SelectMany_with_selecting_outer_element(bool async)
=> Assert.Equal(
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
() => base.SelectMany_with_selecting_outer_element(async))).Message);
}
}

0 comments on commit 8fff1f6

Please sign in to comment.