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

[automated] Merge branch 'release/3.1' => 'master' #17929

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Convert ToList().Count() to AsQueryable().Count()
When enumerable operators appears after ToList, we can convert them to queryable operators by removing ToList.
Also take care when ToList uses a different generic type than inner type.
Avoid adding AsQueryable if inner is already queryable of correct type.

Resolves #13744
  • Loading branch information
smitpatel committed Sep 19, 2019
commit c7f23a75f13b2c32b359fccc2d3aa257e641d21d
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal
{
public class EnumerableToQueryableMethodConvertingExpressionVisitor : ExpressionVisitor
{
private readonly MethodInfo _enumerableToListMethodInfo = typeof(Enumerable).GetTypeInfo()
.GetDeclaredMethods(nameof(Enumerable.ToList)).Single(mi => mi.GetParameters().Length == 1);

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.DeclaringType == typeof(Enumerable))
Expand Down Expand Up @@ -99,12 +102,27 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp

if (CanConvertEnumerableToQueryable(enumerableParameterType, queryableParameterType))
{
if (arguments[i].Type.TryGetElementType(typeof(IQueryable<>)) == null)
var innerArgument = arguments[i];
var genericType = innerArgument.Type.TryGetSequenceType();

// If innerArgument has ToList applied to it then unwrap it.
// Also preserve generic argument of ToList is applied to different type
if (arguments[i].Type.TryGetElementType(typeof(List<>)) != null
&& arguments[i] is MethodCallExpression toListMethodCallExpression
&& toListMethodCallExpression.Method.IsGenericMethod
&& toListMethodCallExpression.Method.GetGenericMethodDefinition() == _enumerableToListMethodInfo)
{
genericType = toListMethodCallExpression.Method.GetGenericArguments()[0];
innerArgument = toListMethodCallExpression.Arguments[0];
}

var innerQueryableElementType = innerArgument.Type.TryGetElementType(typeof(IQueryable<>));
if (innerQueryableElementType == null
|| innerQueryableElementType != genericType)
{
arguments[i] = Expression.Call(
QueryableMethods.AsQueryable.MakeGenericMethod(
arguments[i].Type.TryGetSequenceType()),
arguments[i]);
QueryableMethods.AsQueryable.MakeGenericMethod(genericType),
innerArgument);
}

continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -877,5 +877,11 @@ public override Task Filtered_collection_projection_with_to_list_is_tracked(bool
{
return base.Filtered_collection_projection_with_to_list_is_tracked(isAsync);
}

[ConditionalTheory(Skip = "Issue#17246")]
public override Task ToList_Count_in_projection_works(bool isAsync)
{
return base.ToList_Count_in_projection_works(isAsync);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1399,5 +1399,26 @@ public virtual Task Collection_FirstOrDefault_with_nullable_unsigned_int_column(
isAsync,
cs => cs.Select(c => c.Orders.OrderBy(o => o.OrderID).Select(o => o.EmployeeID).FirstOrDefault()));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task ToList_Count_in_projection_works(bool isAsync)
{
return AssertQuery<Customer>(
isAsync,
cs => cs.Where(c => c.CustomerID.StartsWith("A"))
.Select(c => new
{
c,
Count = c.Orders.ToList().Count()
}),
entryCount: 4,
elementSorter: r => r.c.CustomerID,
elementAsserter: (e, a) =>
{
AssertEqual(e.c, a.c);
Assert.Equal(e.Count, a.Count);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1170,5 +1170,18 @@ FROM [Orders] AS [o]
ORDER BY [o].[OrderID])
FROM [Customers] AS [c]");
}

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

AssertSql(
@"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], (
SELECT COUNT(*)
FROM [Orders] AS [o]
WHERE ([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL) AS [Count]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'A%'");
}
}
}