Skip to content

Commit

Permalink
Query: Assign proper type to CollectionResultExpression (#27134)
Browse files Browse the repository at this point in the history
We copied type from subquery itself which is always Queryable type because of conversion we do. We need to convert it back to enumerable.
Issue happens only when nested level because we use element type so for 1 level queryable is gone. But for nested level we get Queryable<T> as element type on outer which is not assignable from List<T>
The conversion doesn't cause issue with user having Queryable in the result since that throws exception much earlier.

Resolves #27105
  • Loading branch information
smitpatel committed Jan 10, 2022
1 parent 16e1795 commit 100b207
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio
_queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
materializeCollectionNavigationExpression.Subquery)!);
return new CollectionResultExpression(
// expression.Type will be CLR type of the navigation here so that is fine.
new ProjectionBindingExpression(_selectExpression, _clientProjections.Count - 1, expression.Type),
materializeCollectionNavigationExpression.Navigation,
materializeCollectionNavigationExpression.Navigation.ClrType.GetSequenceType());
Expand All @@ -176,6 +177,7 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio
if (subquery != null)
{
_clientProjections!.Add(subquery);
// expression.Type here will be List<T>
return new CollectionResultExpression(
new ProjectionBindingExpression(_selectExpression, _clientProjections.Count - 1, expression.Type),
navigation: null,
Expand All @@ -195,8 +197,17 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio
}

_clientProjections!.Add(subquery);
var type = expression.Type;
if (!(AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue27105", out var enabled) && enabled))
{
if (type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(IQueryable<>))
{
type = typeof(IEnumerable<>).MakeGenericType(type.GetSequenceType());
}
}
var projectionBindingExpression = new ProjectionBindingExpression(
_selectExpression, _clientProjections.Count - 1, expression.Type);
_selectExpression, _clientProjections.Count - 1, type);
return subquery.ResultCardinality == ResultCardinality.Enumerable
? new CollectionResultExpression(
projectionBindingExpression, navigation: null, subquery.ShaperExpression.Type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,12 @@ public override Task MemberInit_in_projection_without_arguments(bool async)
return base.MemberInit_in_projection_without_arguments(async);
}

[ConditionalTheory(Skip = "Cross collection join Issue#17246")]
public override Task List_of_list_of_anonymous_type(bool async)
{
return base.List_of_list_of_anonymous_type(async);
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2446,5 +2446,31 @@ public virtual Task MemberInit_in_projection_without_arguments(bool async)
AssertEqual(e.Orders.Count(), a.Orders.Count());
});
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task List_of_list_of_anonymous_type(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>()
.Where(c => c.CustomerID.StartsWith("F"))
.Select(c => new
{
c.CustomerID,
ListWithSubList = c.Orders.OrderBy(e => e.OrderID).Select(o => o.OrderDetails.Select(e => new
{
e.OrderID,
e.ProductID
}))
}),
elementSorter: e => e.CustomerID,
elementAsserter: (e, a) =>
{
AssertEqual(e.CustomerID, a.CustomerID);
AssertCollection(e.ListWithSubList, a.ListWithSubList, ordered: true,
elementAsserter: (ee, aa) => AssertCollection(ee, aa, elementSorter: i => (i.OrderID, i.ProductID)));
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,22 @@ WHERE [c].[CustomerID] LIKE N'F%'
ORDER BY [c].[CustomerID]");
}

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

AssertSql(
@"SELECT [c].[CustomerID], [t].[OrderID], [t].[OrderID0], [t].[ProductID]
FROM [Customers] AS [c]
LEFT JOIN (
SELECT [o].[OrderID], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o].[CustomerID]
FROM [Orders] AS [o]
LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID]
) AS [t] ON [c].[CustomerID] = [t].[CustomerID]
WHERE [c].[CustomerID] LIKE N'F%'
ORDER BY [c].[CustomerID], [t].[OrderID], [t].[OrderID0]");
}

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

Expand Down

0 comments on commit 100b207

Please sign in to comment.