Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Allow ToList on non queryables #21502

Merged
1 commit merged into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,28 +128,35 @@ public override Expression Visit(Expression expression)
&& methodCallExpression.Method.DeclaringType == typeof(Enumerable)
&& methodCallExpression.Method.Name == nameof(Enumerable.ToList))
{
return AddCollectionProjection(
_queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
methodCallExpression.Arguments[0]),
null,
methodCallExpression.Method.GetGenericArguments()[0]);
}
var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
methodCallExpression.Arguments[0]);

var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);
if (subquery != null)
if (subqueryTranslation != null)
{
return AddCollectionProjection(
subqueryTranslation,
null,
methodCallExpression.Method.GetGenericArguments()[0]);
}
}
else
{
if (subquery.ResultCardinality == ResultCardinality.Enumerable)
var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);
if (subquery != null)
{
return AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
if (subquery.ResultCardinality == ResultCardinality.Enumerable)
{
return AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
}

return new SingleResultShaperExpression(
new ProjectionBindingExpression(
_queryExpression,
_queryExpression.AddSubqueryProjection(subquery, out var innerShaper),
typeof(ValueBuffer)),
innerShaper,
subquery.ShaperExpression.Type);
}

return new SingleResultShaperExpression(
new ProjectionBindingExpression(
_queryExpression,
_queryExpression.AddSubqueryProjection(subquery, out var innerShaper),
typeof(ValueBuffer)),
innerShaper,
subquery.ShaperExpression.Type);
}

break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,39 +167,43 @@ public override Expression Visit(Expression expression)
var result = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
methodCallExpression.Arguments[0]);

return _selectExpression.AddCollectionProjection(result, null, elementType);
}

var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);

if (subquery != null)
{
if (subquery.ResultCardinality == ResultCardinality.Enumerable)
if (result != null)
{
return _selectExpression.AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
return _selectExpression.AddCollectionProjection(result, null, elementType);
}

static bool IsAggregateResultWithCustomShaper(MethodInfo method)
}
else
{
var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);
if (subquery != null)
{
if (method.IsGenericMethod)
if (subquery.ResultCardinality == ResultCardinality.Enumerable)
{
method = method.GetGenericMethodDefinition();
return _selectExpression.AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
}

return QueryableMethods.IsAverageWithoutSelector(method)
|| QueryableMethods.IsAverageWithSelector(method)
|| method == QueryableMethods.MaxWithoutSelector
|| method == QueryableMethods.MaxWithSelector
|| method == QueryableMethods.MinWithoutSelector
|| method == QueryableMethods.MinWithSelector
|| QueryableMethods.IsSumWithoutSelector(method)
|| QueryableMethods.IsSumWithSelector(method);
}
static bool IsAggregateResultWithCustomShaper(MethodInfo method)
{
if (method.IsGenericMethod)
{
method = method.GetGenericMethodDefinition();
}

return QueryableMethods.IsAverageWithoutSelector(method)
|| QueryableMethods.IsAverageWithSelector(method)
|| method == QueryableMethods.MaxWithoutSelector
|| method == QueryableMethods.MaxWithSelector
|| method == QueryableMethods.MinWithoutSelector
|| method == QueryableMethods.MinWithSelector
|| QueryableMethods.IsSumWithoutSelector(method)
|| QueryableMethods.IsSumWithSelector(method);
}

if (!(subquery.ShaperExpression is ProjectionBindingExpression
|| IsAggregateResultWithCustomShaper(methodCallExpression.Method)))
{
return _selectExpression.AddSingleProjection(subquery);
if (!(subquery.ShaperExpression is ProjectionBindingExpression
|| IsAggregateResultWithCustomShaper(methodCallExpression.Method)))
{
return _selectExpression.AddSingleProjection(subquery);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ public virtual ShapedQueryExpression TranslateSubquery([NotNull] Expression expr
Check.NotNull(expression, nameof(expression));

var subqueryVisitor = CreateSubqueryVisitor();
var translation = (ShapedQueryExpression)subqueryVisitor.Visit(expression);
var translation = subqueryVisitor.Visit(expression) as ShapedQueryExpression;
if (translation == null && subqueryVisitor.TranslationErrorDetails != null)
{
AddTranslationErrorDetails(subqueryVisitor.TranslationErrorDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5991,6 +5991,39 @@ public virtual Task Select_distinct_Select_with_client_bindings(bool async)
.Select(e => new DTO<int> { Property = ClientMethod(e) }));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task ToList_over_string(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().OrderBy(c => c.CustomerID).Select(e => new { Property = e.City.ToList() }),
assertOrder: true,
elementAsserter: (e,a) => Assert.True(e.Property.SequenceEqual(a.Property)));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task ToArray_over_string(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().OrderBy(c => c.CustomerID).Select(e => new { Property = e.City.ToArray() }),
assertOrder: true,
elementAsserter: (e, a) => Assert.True(e.Property.SequenceEqual(a.Property)));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task AsEnumerable_over_string(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().OrderBy(c => c.CustomerID).Select(e => new { Property = e.City.AsEnumerable() }),
assertOrder: true,
elementAsserter: (e, a) => Assert.True(e.Property.SequenceEqual(a.Property)));
}

private static int ClientMethod(int s) => s;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5133,6 +5133,36 @@ WHERE [o].[OrderID] < 10000
) AS [t]");
}

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

AssertSql(
@"SELECT [c].[City]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]");
}

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

AssertSql(
@"SELECT [c].[City]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]");
}

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

AssertSql(
@"SELECT [c].[City]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]");
}

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

Expand Down