Skip to content
Open
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 @@ -178,6 +178,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
visitedExpression = TryConvertCollectionContainsToQueryableContains(methodCallExpression);
}

if (method.Name == nameof(List<>.Exists)
&& method.DeclaringType is { IsGenericType: true } existsDeclaringType
&& existsDeclaringType.GetGenericTypeDefinition() == typeof(List<>))
{
visitedExpression = TryConvertListExistsToQueryableAny(methodCallExpression);
}

if (method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
&& method.Name is nameof(EntityFrameworkQueryableExtensions.Include)
or nameof(EntityFrameworkQueryableExtensions.ThenInclude)
Expand Down Expand Up @@ -506,6 +513,35 @@ private Expression TryConvertEnumerableToQueryable(MethodCallExpression methodCa
return methodCallExpression.Update(Visit(methodCallExpression.Object), arguments);
}

private Expression TryConvertListExistsToQueryableAny(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Object is MemberInitExpression or NewExpression)
{
return base.VisitMethodCall(methodCallExpression);
}

// List<T>.Exists takes a Predicate<T>; rewrite the lambda to Func<T, bool> so it matches
// Queryable.Any's Expression<Func<T, bool>> parameter.
if (methodCallExpression.Arguments[0] is not LambdaExpression predicateLambda)
{
return base.VisitMethodCall(methodCallExpression);
}

var sourceType = methodCallExpression.Method.DeclaringType!.GetGenericArguments()[0];
var rewrittenPredicate = Expression.Lambda(
typeof(Func<,>).MakeGenericType(sourceType, typeof(bool)),
predicateLambda.Body,
predicateLambda.Parameters);

return VisitMethodCall(
Expression.Call(
QueryableMethods.AnyWithPredicate.MakeGenericMethod(sourceType),
Expression.Call(
QueryableMethods.AsQueryable.MakeGenericMethod(sourceType),
methodCallExpression.Object!),
Expression.Quote(rewrittenPredicate)));
}

private Expression TryConvertCollectionContainsToQueryableContains(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Object is MemberInitExpression or NewExpression)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,28 +1103,32 @@ public override async Task Where_Join_Any(bool async)

public override async Task Where_Join_Exists(bool async)
{
await base.Where_Join_Exists(async);
// Cosmos client evaluation. Issue #17246.
await AssertTranslationFailed(() => base.Where_Join_Exists(async));

AssertSql();
}

public override async Task Where_Join_Exists_Inequality(bool async)
{
await base.Where_Join_Exists_Inequality(async);
// Cosmos client evaluation. Issue #17246.
await AssertTranslationFailed(() => base.Where_Join_Exists_Inequality(async));

AssertSql();
}

public override async Task Where_Join_Exists_Constant(bool async)
{
await base.Where_Join_Exists_Constant(async);
// Cosmos client evaluation. Issue #17246.
await AssertTranslationFailed(() => base.Where_Join_Exists_Constant(async));

AssertSql();
}

public override async Task Where_Join_Not_Exists(bool async)
{
await base.Where_Join_Not_Exists(async);
// Cosmos client evaluation. Issue #17246.
await AssertTranslationFailed(() => base.Where_Join_Not_Exists(async));

AssertSql();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1942,33 +1942,31 @@ public virtual Task Where_Join_Any(bool async)

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Where_Join_Exists(bool async)
// Translate List.Exists. Issue #17762.
=> AssertTranslationFailed(() => AssertQuery(
=> AssertQuery(
async,
ss => ss.Set<Customer>()
.Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => o.OrderDate == new DateTime(2008, 10, 24)))));
.Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => o.OrderDate == new DateTime(2008, 10, 24))),
assertEmpty: true);

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Where_Join_Exists_Inequality(bool async)
// Translate List.Exists. Issue #17762.
=> AssertTranslationFailed(() => AssertQuery(
=> AssertQuery(
async,
ss => ss.Set<Customer>()
.Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => o.OrderDate != new DateTime(2008, 10, 24)))));
.Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => o.OrderDate != new DateTime(2008, 10, 24))));

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Where_Join_Exists_Constant(bool async)
// Translate List.Exists. Issue #17762.
=> AssertTranslationFailed(() => AssertQuery(
=> AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => false))));
ss => ss.Set<Customer>().Where(c => c.CustomerID == "ALFKI" && c.Orders.Exists(o => false)),
assertEmpty: true);

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Where_Join_Not_Exists(bool async)
// Translate List.Exists. Issue #17762.
=> AssertTranslationFailed(() => AssertQuery(
=> AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.CustomerID == "ALFKI" && !c.Orders.Exists(o => false))));
ss => ss.Set<Customer>().Where(c => c.CustomerID == "ALFKI" && !c.Orders.Exists(o => false)));

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Multiple_joins_Where_Order_Any(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1964,28 +1964,54 @@ public override async Task Where_Join_Exists(bool async)
{
await base.Where_Join_Exists(async);

AssertSql();
AssertSql(
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] = N'ALFKI' AND EXISTS (
SELECT 1
FROM [Orders] AS [o]
WHERE [c].[CustomerID] = [o].[CustomerID] AND [o].[OrderDate] = '2008-10-24T00:00:00.000')
""");
}

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

AssertSql();
AssertSql(
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] = N'ALFKI' AND EXISTS (
SELECT 1
FROM [Orders] AS [o]
WHERE [c].[CustomerID] = [o].[CustomerID] AND ([o].[OrderDate] <> '2008-10-24T00:00:00.000' OR [o].[OrderDate] IS NULL))
""");
}

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

AssertSql();
AssertSql(
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE 0 = 1
""");
}

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

AssertSql();
AssertSql(
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] = N'ALFKI'
""");
}

public override async Task Join_OrderBy_Count(bool async)
Expand Down
Loading