diff --git a/src/EFCore/Query/Internal/FunctionPreprocessingExpressionVisitor.cs b/src/EFCore/Query/Internal/FunctionPreprocessingExpressionVisitor.cs
deleted file mode 100644
index 62699b065a0..00000000000
--- a/src/EFCore/Query/Internal/FunctionPreprocessingExpressionVisitor.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Utilities;
-
-namespace Microsoft.EntityFrameworkCore.Query.Internal
-{
- public class FunctionPreprocessingExpressionVisitor : ExpressionVisitor
- {
- private static readonly MethodInfo _startsWithMethodInfo
- = typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) });
-
- private static readonly MethodInfo _endsWithMethodInfo
- = typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) });
-
- private static readonly Expression _constantNullString = Expression.Constant(null, typeof(string));
-
- protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
-
- if (_startsWithMethodInfo.Equals(methodCallExpression.Method)
- || _endsWithMethodInfo.Equals(methodCallExpression.Method))
- {
- if (methodCallExpression.Arguments[0] is ConstantExpression constantArgument
- && (string)constantArgument.Value == string.Empty)
- {
- // every string starts/ends with empty string.
- return Expression.Constant(true);
- }
-
- var newObject = Visit(methodCallExpression.Object);
- var newArgument = Visit(methodCallExpression.Arguments[0]);
-
- var result = Expression.AndAlso(
- Expression.NotEqual(newObject, _constantNullString),
- Expression.AndAlso(
- Expression.NotEqual(newArgument, _constantNullString),
- methodCallExpression.Update(newObject, new[] { newArgument })));
-
- return newArgument is ConstantExpression
- ? result
- : Expression.OrElse(
- Expression.Equal(
- newArgument,
- Expression.Constant(string.Empty)),
- result);
- }
-
- return base.VisitMethodCall(methodCallExpression);
- }
-
- protected override Expression VisitUnary(UnaryExpression unaryExpression)
- {
- Check.NotNull(unaryExpression, nameof(unaryExpression));
-
- if (unaryExpression.NodeType == ExpressionType.Not
- && unaryExpression.Operand is MethodCallExpression innerMethodCall
- && (_startsWithMethodInfo.Equals(innerMethodCall.Method)
- || _endsWithMethodInfo.Equals(innerMethodCall.Method)))
- {
- if (innerMethodCall.Arguments[0] is ConstantExpression constantArgument
- && (string)constantArgument.Value == string.Empty)
- {
- // every string starts/ends with empty string.
- return Expression.Constant(false);
- }
-
- var newObject = Visit(innerMethodCall.Object);
- var newArgument = Visit(innerMethodCall.Arguments[0]);
-
- var result = Expression.AndAlso(
- Expression.NotEqual(newObject, _constantNullString),
- Expression.AndAlso(
- Expression.NotEqual(newArgument, _constantNullString),
- Expression.Not(innerMethodCall.Update(newObject, new[] { newArgument }))));
-
- return newArgument is ConstantExpression
- ? result
- : Expression.AndAlso(
- Expression.NotEqual(
- newArgument,
- Expression.Constant(string.Empty)),
- result);
- }
-
- return base.VisitUnary(unaryExpression);
- }
- }
-}
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
index 3341a0929c6..fbaf27181a0 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
@@ -178,9 +178,9 @@ protected override Expression VisitMember(MemberExpression memberExpression)
if (innerQueryable.Type.TryGetElementType(typeof(IQueryable<>)) != null)
{
return Visit(
- Expression.Call(
- QueryableMethods.CountWithoutPredicate.MakeGenericMethod(innerQueryable.Type.TryGetSequenceType()),
- innerQueryable));
+ Expression.Call(
+ QueryableMethods.CountWithoutPredicate.MakeGenericMethod(innerQueryable.Type.TryGetSequenceType()),
+ innerQueryable));
}
}
@@ -528,13 +528,8 @@ when QueryableMethods.IsSumWithSelector(method):
&& (method.GetGenericMethodDefinition() == EnumerableMethods.ToList
|| method.GetGenericMethodDefinition() == EnumerableMethods.ToArray))
{
- var argument = Visit(methodCallExpression.Arguments[0]);
- if (argument is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression)
- {
- argument = materializeCollectionNavigationExpression.Subquery;
- }
-
- return methodCallExpression.Update(null, new[] { argument });
+ return methodCallExpression.Update(
+ null, new[] { UnwrapCollectionMaterialization(Visit(methodCallExpression.Arguments[0])) });
}
return ProcessUnknownMethod(methodCallExpression);
@@ -1584,16 +1579,14 @@ private LambdaExpression GenerateLambda(Expression body, ParameterExpression cur
private Expression UnwrapCollectionMaterialization(Expression expression)
{
- if (expression is MethodCallExpression innerMethodCall
- && innerMethodCall.Method.IsGenericMethod)
+ while (expression is MethodCallExpression innerMethodCall
+ && innerMethodCall.Method.IsGenericMethod
+ && innerMethodCall.Method.GetGenericMethodDefinition() is MethodInfo innerMethod
+ && (innerMethod == EnumerableMethods.AsEnumerable
+ || innerMethod == EnumerableMethods.ToList
+ || innerMethod == EnumerableMethods.ToArray))
{
- var innerGenericMethod = innerMethodCall.Method.GetGenericMethodDefinition();
- if (innerGenericMethod == EnumerableMethods.AsEnumerable
- || innerGenericMethod == EnumerableMethods.ToList
- || innerGenericMethod == EnumerableMethods.ToArray)
- {
- expression = innerMethodCall.Arguments[0];
- }
+ expression = innerMethodCall.Arguments[0];
}
if (expression is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression)
diff --git a/src/EFCore/Query/Internal/AllAnyContainsRewritingExpressionVisitor.cs b/src/EFCore/Query/Internal/QueryOptimizingExpressionVisitor.cs
similarity index 50%
rename from src/EFCore/Query/Internal/AllAnyContainsRewritingExpressionVisitor.cs
rename to src/EFCore/Query/Internal/QueryOptimizingExpressionVisitor.cs
index b6c50efafa3..b8650b371b5 100644
--- a/src/EFCore/Query/Internal/AllAnyContainsRewritingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/QueryOptimizingExpressionVisitor.cs
@@ -9,16 +9,51 @@
namespace Microsoft.EntityFrameworkCore.Query.Internal
{
- public class AllAnyContainsRewritingExpressionVisitor : ExpressionVisitor
+ public class QueryOptimizingExpressionVisitor : ExpressionVisitor
{
- private static bool IsExpressionOfFunc(Type type, int funcGenericArgs = 2)
- => type.IsGenericType
- && type.GetGenericArguments().Length == funcGenericArgs;
+ private static readonly MethodInfo _stringCompareWithComparisonMethod =
+ typeof(string).GetRuntimeMethod(nameof(string.Compare), new[] { typeof(string), typeof(string), typeof(StringComparison) });
+ private static readonly MethodInfo _stringCompareWithoutComparisonMethod =
+ typeof(string).GetRuntimeMethod(nameof(string.Compare), new[] { typeof(string), typeof(string) });
+ private static readonly MethodInfo _startsWithMethodInfo =
+ typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) });
+ private static readonly MethodInfo _endsWithMethodInfo =
+ typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) });
+
+ private static readonly Expression _constantNullString = Expression.Constant(null, typeof(string));
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
Check.NotNull(methodCallExpression, nameof(methodCallExpression));
+ if (_startsWithMethodInfo.Equals(methodCallExpression.Method)
+ || _endsWithMethodInfo.Equals(methodCallExpression.Method))
+ {
+ if (methodCallExpression.Arguments[0] is ConstantExpression constantArgument
+ && (string)constantArgument.Value == string.Empty)
+ {
+ // every string starts/ends with empty string.
+ return Expression.Constant(true);
+ }
+
+ var newObject = Visit(methodCallExpression.Object);
+ var newArgument = Visit(methodCallExpression.Arguments[0]);
+
+ var result = Expression.AndAlso(
+ Expression.NotEqual(newObject, _constantNullString),
+ Expression.AndAlso(
+ Expression.NotEqual(newArgument, _constantNullString),
+ methodCallExpression.Update(newObject, new[] { newArgument })));
+
+ return newArgument is ConstantExpression
+ ? result
+ : Expression.OrElse(
+ Expression.Equal(
+ newArgument,
+ Expression.Constant(string.Empty)),
+ result);
+ }
+
if (methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Method.GetGenericMethodDefinition() is MethodInfo methodInfo
&& (methodInfo.Equals(EnumerableMethods.AnyWithPredicate) || methodInfo.Equals(EnumerableMethods.All))
@@ -46,9 +81,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
if (methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Method.GetGenericMethodDefinition() is MethodInfo containsMethodInfo
- && containsMethodInfo.Equals(QueryableMethods.Contains)
- // special case Queryable.Contains(byte_array, byte) - we don't want those to be rewritten
- && methodCallExpression.Arguments[1].Type != typeof(byte))
+ && containsMethodInfo.Equals(QueryableMethods.Contains))
{
var typeArgument = methodCallExpression.Method.GetGenericArguments()[0];
var anyMethod = QueryableMethods.AnyWithPredicate.MakeGenericMethod(typeArgument);
@@ -63,7 +96,67 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
return Expression.Call(null, anyMethod, new[] { methodCallExpression.Arguments[0], anyLambda });
}
- return base.VisitMethodCall(methodCallExpression);
+ var visited = (MethodCallExpression)base.VisitMethodCall(methodCallExpression);
+
+ // In VB.NET, comparison operators between strings (equality, greater-than, less-than) yield
+ // calls to a VB-specific CompareString method. Normalize that to string.Compare.
+ if (visited.Method.Name == "CompareString"
+ && visited.Method.DeclaringType?.Name == "Operators"
+ && visited.Method.DeclaringType?.Namespace == "Microsoft.VisualBasic.CompilerServices"
+ && visited.Object == null
+ && visited.Arguments.Count == 3
+ && visited.Arguments[2] is ConstantExpression textCompareConstantExpression)
+ {
+ return (bool)textCompareConstantExpression.Value
+ ? Expression.Call(
+ _stringCompareWithComparisonMethod,
+ visited.Arguments[0],
+ visited.Arguments[1],
+ Expression.Constant(StringComparison.OrdinalIgnoreCase))
+ : Expression.Call(
+ _stringCompareWithoutComparisonMethod,
+ visited.Arguments[0],
+ visited.Arguments[1]);
+ }
+
+ return visited;
+ }
+
+ protected override Expression VisitUnary(UnaryExpression unaryExpression)
+ {
+ Check.NotNull(unaryExpression, nameof(unaryExpression));
+
+ if (unaryExpression.NodeType == ExpressionType.Not
+ && unaryExpression.Operand is MethodCallExpression innerMethodCall
+ && (_startsWithMethodInfo.Equals(innerMethodCall.Method)
+ || _endsWithMethodInfo.Equals(innerMethodCall.Method)))
+ {
+ if (innerMethodCall.Arguments[0] is ConstantExpression constantArgument
+ && (string)constantArgument.Value == string.Empty)
+ {
+ // every string starts/ends with empty string.
+ return Expression.Constant(false);
+ }
+
+ var newObject = Visit(innerMethodCall.Object);
+ var newArgument = Visit(innerMethodCall.Arguments[0]);
+
+ var result = Expression.AndAlso(
+ Expression.NotEqual(newObject, _constantNullString),
+ Expression.AndAlso(
+ Expression.NotEqual(newArgument, _constantNullString),
+ Expression.Not(innerMethodCall.Update(newObject, new[] { newArgument }))));
+
+ return newArgument is ConstantExpression
+ ? result
+ : Expression.AndAlso(
+ Expression.NotEqual(
+ newArgument,
+ Expression.Constant(string.Empty)),
+ result);
+ }
+
+ return base.VisitUnary(unaryExpression);
}
private bool TryExtractEqualityOperands(Expression expression, out Expression left, out Expression right, out bool negated)
diff --git a/src/EFCore/Query/Internal/VBToCSharpConvertingExpressionVisitor.cs b/src/EFCore/Query/Internal/VBToCSharpConvertingExpressionVisitor.cs
deleted file mode 100644
index 0b8a64e6d9a..00000000000
--- a/src/EFCore/Query/Internal/VBToCSharpConvertingExpressionVisitor.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq.Expressions;
-using System.Reflection;
-
-namespace Microsoft.EntityFrameworkCore.Query.Internal
-{
- ///
- /// Normalizes certain language-specific aspects of the expression trees produced by languages other
- /// than C#, e.g. Visual Basic.
- ///
- public class VBToCSharpConvertingExpressionVisitor : ExpressionVisitor
- {
- private static readonly MethodInfo _stringCompareWithComparisonMethod
- = typeof(string).GetRuntimeMethod(
- nameof(string.Compare),
- new[] { typeof(string), typeof(string), typeof(StringComparison) });
-
- private static readonly MethodInfo _stringCompareWithoutComparisonMethod
- = typeof(string).GetRuntimeMethod(
- nameof(string.Compare),
- new[] { typeof(string), typeof(string) });
-
- protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
- {
- var visited = (MethodCallExpression)base.VisitMethodCall(methodCallExpression);
-
- // In VB.NET, comparison operators between strings (equality, greater-than, less-than) yield
- // calls to a VB-specific CompareString method. Normalize that to string.Compare.
- if (visited.Method.Name == "CompareString"
- && visited.Method.DeclaringType?.Name == "Operators"
- && visited.Method.DeclaringType?.Namespace == "Microsoft.VisualBasic.CompilerServices"
- && visited.Object == null
- && visited.Arguments.Count == 3
- && visited.Arguments[2] is ConstantExpression textCompareConstantExpression)
- {
- return (bool)textCompareConstantExpression.Value
- ? Expression.Call(
- _stringCompareWithComparisonMethod,
- visited.Arguments[0],
- visited.Arguments[1],
- Expression.Constant(StringComparison.OrdinalIgnoreCase))
- : Expression.Call(
- _stringCompareWithoutComparisonMethod,
- visited.Arguments[0],
- visited.Arguments[1]);
- }
-
- return visited;
- }
- }
-}
diff --git a/src/EFCore/Query/QueryTranslationPreprocessor.cs b/src/EFCore/Query/QueryTranslationPreprocessor.cs
index fcaed1b632f..52c52836a1a 100644
--- a/src/EFCore/Query/QueryTranslationPreprocessor.cs
+++ b/src/EFCore/Query/QueryTranslationPreprocessor.cs
@@ -30,16 +30,12 @@ public virtual Expression Process([NotNull] Expression query)
Check.NotNull(query, nameof(query));
query = new InvocationExpressionRemovingExpressionVisitor().Visit(query);
-
query = NormalizeQueryableMethodCall(query);
-
- query = new VBToCSharpConvertingExpressionVisitor().Visit(query);
- query = new AllAnyContainsRewritingExpressionVisitor().Visit(query);
query = new NullCheckRemovingExpressionVisitor().Visit(query);
query = new SubqueryMemberPushdownExpressionVisitor(QueryCompilationContext.Model).Visit(query);
query = new NavigationExpandingExpressionVisitor(this, QueryCompilationContext, Dependencies.EvaluatableExpressionFilter)
.Expand(query);
- query = new FunctionPreprocessingExpressionVisitor().Visit(query);
+ query = new QueryOptimizingExpressionVisitor().Visit(query);
return query;
}
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
index dfefc837c10..bb8e5fde3e4 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
@@ -2176,7 +2176,7 @@ public virtual Task Where_collection_navigation_ToArray_Count(bool async)
elementAsserter: (e, a) => AssertCollection(e, a));
}
- [ConditionalTheory(Skip = "Issue#19433")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_collection_navigation_ToArray_Contains(bool async)
{
@@ -2185,7 +2185,7 @@ public virtual Task Where_collection_navigation_ToArray_Contains(bool async)
return AssertQuery(
async,
ss => ss.Set()
- .Select(c => c.Orders.ToArray())
+ .Select(c => c.Orders.AsEnumerable().ToArray())
.Where(e => e.Contains(order)),
entryCount: 5);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
index 6bcbd2a10f3..608aac3c47a 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
@@ -2190,9 +2190,13 @@ public override async Task Contains_with_subquery_optional_navigation_and_consta
FROM [LevelOne] AS [l]
LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id]
WHERE EXISTS (
- SELECT DISTINCT 1
- FROM [LevelThree] AS [l1]
- WHERE ([l0].[Id] IS NOT NULL AND ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id])) AND ([l1].[Id] = 1))");
+ SELECT 1
+ FROM (
+ SELECT DISTINCT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE [l0].[Id] IS NOT NULL AND ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id])
+ ) AS [t]
+ WHERE [t].[Id] = 1)");
}
public override async Task Contains_with_subquery_optional_navigation_scalar_distinct_and_constant_item(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index 437c8f5d42b..dad3d66d794 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -6807,10 +6807,10 @@ public override async Task Contains_on_collection_of_byte_subquery(bool async)
AssertSql(
@"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId]
FROM [LocustLeaders] AS [l]
-WHERE [l].[ThreatLevelByte] IN (
- SELECT [l0].[ThreatLevelByte]
+WHERE EXISTS (
+ SELECT 1
FROM [LocustLeaders] AS [l0]
-)");
+ WHERE [l0].[ThreatLevelByte] = [l].[ThreatLevelByte])");
}
public override async Task Contains_on_collection_of_nullable_byte_subquery(bool async)
@@ -6873,10 +6873,10 @@ FROM [LocustLeaders] AS [l]
CROSS APPLY (
SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
FROM [Gears] AS [g]
- WHERE [l].[ThreatLevelByte] IN (
- SELECT [l0].[ThreatLevelByte]
+ WHERE EXISTS (
+ SELECT 1
FROM [LocustLeaders] AS [l0]
- )
+ WHERE [l0].[ThreatLevelByte] = [l].[ThreatLevelByte])
) AS [t]");
}
@@ -6890,10 +6890,10 @@ FROM [LocustLeaders] AS [l]
CROSS APPLY (
SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
FROM [Gears] AS [g]
- WHERE [l].[ThreatLevelByte] NOT IN (
- SELECT [l0].[ThreatLevelByte]
+ WHERE NOT (EXISTS (
+ SELECT 1
FROM [LocustLeaders] AS [l0]
- )
+ WHERE [l0].[ThreatLevelByte] = [l].[ThreatLevelByte]))
) AS [t]");
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
index 73bcc5c66a6..b2b83825c55 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
@@ -1572,14 +1572,14 @@ FROM [Order Details] AS [o]
WHERE EXISTS (
SELECT 1
FROM (
- SELECT TOP(1) [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock]
+ SELECT TOP(1) [p].[ProductID]
FROM [Products] AS [p]
ORDER BY [p].[ProductID]
) AS [t]
WHERE [t].[ProductID] = [o].[ProductID]) OR EXISTS (
SELECT 1
FROM (
- SELECT TOP(1) [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate]
+ SELECT TOP(1) [o0].[OrderID]
FROM [Orders] AS [o0]
ORDER BY [o0].[OrderID]
) AS [t0]
@@ -1596,14 +1596,14 @@ FROM [Order Details] AS [o]
WHERE EXISTS (
SELECT 1
FROM (
- SELECT TOP(20) [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock]
+ SELECT TOP(20) [p].[ProductID]
FROM [Products] AS [p]
ORDER BY [p].[ProductID]
) AS [t]
WHERE [t].[ProductID] = [o].[ProductID]) AND EXISTS (
SELECT 1
FROM (
- SELECT TOP(10) [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate]
+ SELECT TOP(10) [o0].[OrderID]
FROM [Orders] AS [o0]
ORDER BY [o0].[OrderID]
) AS [t0]
@@ -1966,7 +1966,17 @@ public override async Task Where_collection_navigation_ToArray_Contains(bool asy
{
await base.Where_collection_navigation_ToArray_Contains(async);
- AssertSql(" ");
+ AssertSql(
+ @"@__entity_equality_order_0_OrderID='10248' (Nullable = true)
+
+SELECT [c].[CustomerID], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
+FROM [Customers] AS [c]
+LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID]
+WHERE EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o0]
+ WHERE ([c].[CustomerID] = [o0].[CustomerID]) AND ([o0].[OrderID] = @__entity_equality_order_0_OrderID))
+ORDER BY [c].[CustomerID], [o].[OrderID]");
}
public override async Task Where_collection_navigation_AsEnumerable_Count(bool async)