diff --git a/src/EFCore/Query/Internal/EnumerableToQueryableMethodConvertingExpressionVisitor.cs b/src/EFCore/Query/Internal/EnumerableToQueryableMethodConvertingExpressionVisitor.cs index 922b36a70b7..001c34a676f 100644 --- a/src/EFCore/Query/Internal/EnumerableToQueryableMethodConvertingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/EnumerableToQueryableMethodConvertingExpressionVisitor.cs @@ -178,7 +178,8 @@ private static bool ClientSource(Expression expression) => expression is ConstantExpression || expression is MemberInitExpression || expression is NewExpression - || expression is ParameterExpression; + || (expression is ParameterExpression parameter + && parameter.Name.StartsWith(CompiledQueryCache.CompiledQueryParameterPrefix, StringComparison.Ordinal)); private static bool CanConvertEnumerableToQueryable(Type enumerableType, Type queryableType) { @@ -198,19 +199,8 @@ private static bool CanConvertEnumerableToQueryable(Type enumerableType, Type qu enumerableType = enumerableType.GetGenericTypeDefinition(); queryableType = queryableType.GetGenericTypeDefinition(); - if (enumerableType == typeof(IEnumerable<>) - && queryableType == typeof(IQueryable<>)) - { - return true; - } - - if (enumerableType == typeof(IOrderedEnumerable<>) - && queryableType == typeof(IOrderedQueryable<>)) - { - return true; - } - - return false; + return enumerableType == typeof(IEnumerable<>) && queryableType == typeof(IQueryable<>) + || enumerableType == typeof(IOrderedEnumerable<>) && queryableType == typeof(IOrderedQueryable<>); } } } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 7c3cd0c7e44..a6ed534c7cd 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -150,24 +151,18 @@ protected override Expression VisitMember(MemberExpression memberExpression) var innerExpression = Visit(memberExpression.Expression); - // Convert CollectionNavigation.Count to subquery.Count() - if (innerExpression is MaterializeCollectionNavigationExpression materializeCollectionNavigation - && memberExpression.Member.Name == nameof(List.Count)) + // Convert ICollection.Count to Count() + if (memberExpression.Expression != null + && memberExpression.Member.Name == nameof(ICollection.Count) + && memberExpression.Expression.Type.GetInterfaces().Append(memberExpression.Expression.Type) + .Any(e => e.IsGenericType && e.GetGenericTypeDefinition() == typeof(ICollection<>))) { - var subquery = materializeCollectionNavigation.Subquery; - var elementType = subquery.Type.TryGetSequenceType(); - if (subquery is OwnedNavigationReference ownedNavigationReference - && ownedNavigationReference.Navigation.IsCollection()) - { - subquery = Expression.Call( - QueryableMethods.AsQueryable.MakeGenericMethod(elementType), - subquery); - } + var innerQueryable = UnwrapCollectionMaterialization(innerExpression); return Visit( Expression.Call( - QueryableMethods.CountWithoutPredicate.MakeGenericMethod(elementType), - subquery)); + QueryableMethods.CountWithoutPredicate.MakeGenericMethod(innerQueryable.Type.TryGetSequenceType()), + innerQueryable)); } var updatedExpression = (Expression)memberExpression.Update(innerExpression); @@ -493,30 +488,7 @@ when QueryableMethods.IsSumWithSelector(method): if (genericMethod == QueryableMethods.AsQueryable) { - if (firstArgument is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression) - { - var subquery = materializeCollectionNavigationExpression.Subquery; - - return subquery is OwnedNavigationReference innerOwnedNavigationReference - && innerOwnedNavigationReference.Navigation.IsCollection() - ? Visit( - Expression.Call( - QueryableMethods.AsQueryable.MakeGenericMethod(subquery.Type.TryGetSequenceType()), - subquery)) - : subquery; - } - - if (firstArgument is OwnedNavigationReference ownedNavigationReference - && ownedNavigationReference.Navigation.IsCollection()) - { - var parameterName = GetParameterName("o"); - var entityReference = ownedNavigationReference.EntityReference; - var currentTree = new NavigationTreeExpression(entityReference); - - return new NavigationExpansionExpression(methodCallExpression, currentTree, currentTree, parameterName); - } - - return firstArgument; + return UnwrapCollectionMaterialization(firstArgument); } if (firstArgument.Type.TryGetElementType(typeof(IQueryable<>)) == null) @@ -531,8 +503,10 @@ when QueryableMethods.IsSumWithSelector(method): throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print())); } + // Remove MaterializeCollectionNavigationExpression when applying ToList/ToArray if (method.IsGenericMethod - && method.GetGenericMethodDefinition() == EnumerableMethods.ToList) + && (method.GetGenericMethodDefinition() == EnumerableMethods.ToList + || method.GetGenericMethodDefinition() == EnumerableMethods.ToArray)) { var argument = Visit(methodCallExpression.Arguments[0]); if (argument is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression) @@ -564,6 +538,24 @@ when QueryableMethods.IsSumWithSelector(method): return ProcessUnknownMethod(methodCallExpression); } + protected override Expression VisitUnary(UnaryExpression unaryExpression) + { + var operand = Visit(unaryExpression.Operand); + // Convert Array.Length to Count() + if (unaryExpression.Operand.Type.IsArray + && unaryExpression.NodeType == ExpressionType.ArrayLength) + { + var innerQueryable = UnwrapCollectionMaterialization(operand); + // Only if inner is queryable as array properties could also have Length access + if (innerQueryable.Type.TryGetElementType(typeof(IQueryable<>)) is Type elementType) + { + return Visit(Expression.Call(QueryableMethods.CountWithoutPredicate.MakeGenericMethod(elementType), innerQueryable)); + } + } + + return unaryExpression.Update(operand); + } + private Expression ProcessAllAnyCountLongCount( NavigationExpansionExpression source, MethodInfo genericMethod, LambdaExpression predicate) { @@ -757,7 +749,7 @@ private NavigationExpansionExpression ProcessGroupBy( source.Source, Expression.Quote(keySelector), Expression.Quote(elementSelector), - Expression.Quote(resultSelector)); + Expression.Quote(Visit(resultSelector))); var navigationTree = new NavigationTreeExpression(Expression.Default(result.Type.TryGetSequenceType())); var parameterName = GetParameterName("e"); @@ -1278,7 +1270,7 @@ private MethodCallExpression ConvertToEnumerable(MethodInfo queryableMethod, IEn var enumerableMethod = EnumerableMethods.GetMinWithSelector(resultType); enumerableMethod = IsNumericType(resultType) - ? enumerableMethod.MakeGenericMethod(resultType) + ? enumerableMethod.MakeGenericMethod(genericTypeArguments[0]) : enumerableMethod.MakeGenericMethod(genericTypeArguments); return Expression.Call(enumerableMethod, enumerableArguments); @@ -1306,7 +1298,7 @@ private MethodCallExpression ConvertToEnumerable(MethodInfo queryableMethod, IEn var enumerableMethod = EnumerableMethods.GetMaxWithSelector(resultType); enumerableMethod = IsNumericType(resultType) - ? enumerableMethod.MakeGenericMethod(resultType) + ? enumerableMethod.MakeGenericMethod(genericTypeArguments[0]) : enumerableMethod.MakeGenericMethod(genericTypeArguments); return Expression.Call(enumerableMethod, enumerableArguments); @@ -1376,6 +1368,16 @@ private NavigationExpansionExpression CreateNavigationExpansionExpression(Expres return new NavigationExpansionExpression(sourceExpression, currentTree, currentTree, parameterName); } + private NavigationExpansionExpression CreateNavigationExpansionExpression( + Expression sourceExpression, OwnedNavigationReference ownedNavigationReference) + { + var parameterName = GetParameterName("o"); + var entityReference = ownedNavigationReference.EntityReference; + var currentTree = new NavigationTreeExpression(entityReference); + + return new NavigationExpansionExpression(sourceExpression, currentTree, currentTree, parameterName); + } + private Expression ExpandNavigationsForSource(NavigationExpansionExpression source, Expression expression) { expression = new ExpandingExpressionVisitor(this, source).Visit(expression); @@ -1412,6 +1414,35 @@ private static IEnumerable FindNavigations(IEntityType entityType, private LambdaExpression GenerateLambda(Expression body, ParameterExpression currentParameter) => Expression.Lambda(Reduce(body), currentParameter); + private Expression UnwrapCollectionMaterialization(Expression expression) + { + if (expression is MethodCallExpression innerMethodCall + && innerMethodCall.Method.IsGenericMethod) + { + var innerGenericMethod = innerMethodCall.Method.GetGenericMethodDefinition(); + if (innerGenericMethod == EnumerableMethods.AsEnumerable + || innerGenericMethod == EnumerableMethods.ToList + || innerGenericMethod == EnumerableMethods.ToArray) + { + expression = innerMethodCall.Arguments[0]; + } + } + + if (expression is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression) + { + expression = materializeCollectionNavigationExpression.Subquery; + } + + return expression is OwnedNavigationReference ownedNavigationReference + && ownedNavigationReference.Navigation.IsCollection() + ? CreateNavigationExpansionExpression( + Expression.Call( + QueryableMethods.AsQueryable.MakeGenericMethod(ownedNavigationReference.Type.TryGetSequenceType()), + ownedNavigationReference), + ownedNavigationReference) + : expression; + } + private string GetParameterName(string prefix) { var uniqueName = prefix; diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index f98af93a512..5d323cc3648 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -526,15 +526,12 @@ protected override Expression VisitExtension(Expression node) { Check.NotNull(node, nameof(node)); - if (node is ProjectionBindingExpression projectionBindingExpression) - { - return new ProjectionBindingExpression( + return node is ProjectionBindingExpression projectionBindingExpression + ? new ProjectionBindingExpression( _queryExpression, projectionBindingExpression.ProjectionMember.Prepend(_memberShift), - projectionBindingExpression.Type); - } - - return base.VisitExtension(node); + projectionBindingExpression.Type) + : base.VisitExtension(node); } } diff --git a/src/Shared/EnumerableMethods.cs b/src/Shared/EnumerableMethods.cs index 73993e10347..f5b362ddfab 100644 --- a/src/Shared/EnumerableMethods.cs +++ b/src/Shared/EnumerableMethods.cs @@ -20,6 +20,7 @@ internal static class EnumerableMethods public static MethodInfo Contains { get; } public static MethodInfo ToList { get; } + public static MethodInfo ToArray { get; } public static MethodInfo Concat { get; } public static MethodInfo Except { get; } @@ -153,6 +154,8 @@ static EnumerableMethods() ToList = enumerableMethods.Single( mi => mi.Name == nameof(Enumerable.ToList) && mi.GetParameters().Length == 1); + ToArray = enumerableMethods.Single( + mi => mi.Name == nameof(Enumerable.ToArray) && mi.GetParameters().Length == 1); Concat = enumerableMethods.Single( mi => mi.Name == nameof(Enumerable.Concat) && mi.GetParameters().Length == 2); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs index 0bac83d9064..259013c2beb 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs @@ -1082,6 +1082,12 @@ FROM root c ORDER BY c[""EmployeeID""]"); } + [ConditionalTheory(Skip = "Issue#17246")] + public override Task Projection_AsEnumerable_projection(bool async) + { + return base.Projection_AsEnumerable_projection(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs index 93f8d255878..0e9197e998c 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs @@ -1947,6 +1947,102 @@ public override async Task Using_same_parameter_twice_in_query_generates_one_sql AssertSql(" "); } + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToList_Count(bool async) + { + return base.Where_Queryable_ToList_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToList_Contains(bool async) + { + return base.Where_Queryable_ToList_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToArray_Count(bool async) + { + return base.Where_Queryable_ToArray_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToArray_Contains(bool async) + { + return base.Where_Queryable_ToArray_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_AsEnumerable_Count(bool async) + { + return base.Where_Queryable_AsEnumerable_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_AsEnumerable_Contains(bool async) + { + return base.Where_Queryable_AsEnumerable_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToList_Count_member(bool async) + { + return base.Where_Queryable_ToList_Count_member(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_Queryable_ToArray_Length_member(bool async) + { + return base.Where_Queryable_ToArray_Length_member(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToList_Count(bool async) + { + return base.Where_collection_navigation_ToList_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToList_Contains(bool async) + { + return base.Where_collection_navigation_ToList_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToArray_Count(bool async) + { + return base.Where_collection_navigation_ToArray_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToArray_Contains(bool async) + { + return base.Where_collection_navigation_ToArray_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + return base.Where_collection_navigation_AsEnumerable_Count(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_AsEnumerable_Contains(bool async) + { + return base.Where_collection_navigation_AsEnumerable_Contains(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToList_Count_member(bool async) + { + return base.Where_collection_navigation_ToList_Count_member(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Where_collection_navigation_ToArray_Length_member(bool async) + { + return base.Where_collection_navigation_ToArray_Length_member(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs index 8b409af6ccb..386521fb0fd 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs @@ -248,6 +248,36 @@ public override Task Client_method_skip_loads_owned_navigations_variation_2(bool return base.Client_method_skip_loads_owned_navigations_variation_2(async); } + [ConditionalTheory(Skip = "Composition over embedded collection #16926")] + public override Task Where_owned_collection_navigation_ToList_Count(bool async) + { + return base.Where_owned_collection_navigation_ToList_Count(async); + } + + [ConditionalTheory(Skip = "Composition over embedded collection #16926")] + public override Task Where_collection_navigation_ToArray_Count(bool async) + { + return base.Where_collection_navigation_ToArray_Count(async); + } + + [ConditionalTheory(Skip = "Composition over embedded collection #16926")] + public override Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + return base.Where_collection_navigation_AsEnumerable_Count(async); + } + + [ConditionalTheory(Skip = "Composition over embedded collection #16926")] + public override Task Where_collection_navigation_ToList_Count_member(bool async) + { + return base.Where_collection_navigation_ToList_Count_member(async); + } + + [ConditionalTheory(Skip = "Composition over embedded collection #16926")] + public override Task Where_collection_navigation_ToArray_Length_member(bool async) + { + return base.Where_collection_navigation_ToArray_Length_member(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs index a10ae0ad3b9..f9479efbb5f 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -5279,7 +5279,7 @@ orderby ClientEvalSelector(o) group o by o.CustomerID into g orderby g.Key - select g.OrderByDescending(x => x.OrderID), + select g.OrderByDescending(x => x.OrderID).ToList(), assertOrder: true, elementAsserter: (e, a) => AssertCollection(e, a, ordered: true))); } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs index fc1fb7fdd80..b0e0f98db30 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs @@ -1681,5 +1681,21 @@ public virtual Task Project_keyless_entity_FirstOrDefault_without_orderby(bool a async, ss => ss.Set().Select(c => ss.Set().FirstOrDefault(cv => cv.CompanyName == c.CompanyName))); } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_AsEnumerable_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(c => c.CustomerID.StartsWith("A")) + .OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).AsEnumerable()) + .Where(e => e.Where(o => o.OrderID < 11000).Count() > 0) + .Select(e => e.Where(o => o.OrderID < 10750)), + assertOrder: true, + entryCount: 18); + } } } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs index 79002e88e65..cd05e8ce850 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs @@ -1997,95 +1997,219 @@ public virtual Task Using_same_parameter_twice_in_query_generates_one_sql_parame .Select(c => c.CustomerID)); } - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToList_Count(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) - // .Where(e => e.Count() == 0), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToList_Contains(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set() - // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToList()) - // .Where(e => e.Contains("ALFKI")), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToArray_Count(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) - // .Where(e => e.Count() == 0), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToArray_Contains(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set() - // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToArray()) - // .Where(e => e.Contains("ALFKI")), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_AsEnumerable_Count(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).AsEnumerable()) - // .Where(e => e.Count() == 0), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_AsEnumerable_Contains(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set() - // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).AsEnumerable()) - // .Where(e => e.Contains("ALFKI")), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToList_Count_member(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) - // .Where(e => e.Count == 0), - // entryCount: 6); - //} - - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] - //public virtual Task Where_ToArray_Length_member(bool async) - //{ - // return AssertQuery( - // async, - // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) - // .Where(e => e.Length == 0), - // entryCount: 6); - //} + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToList_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToList_Contains(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToList()) + .Where(e => e.Contains("ALFKI"))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToArray_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToArray_Contains(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToArray()) + .Where(e => e.Contains("ALFKI"))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_AsEnumerable_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).AsEnumerable()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_AsEnumerable_Contains(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).AsEnumerable()) + .Where(e => e.Contains("ALFKI"))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToList_Count_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) + .Where(e => e.Count == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory(Skip = "Issue#19431")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_Queryable_ToArray_Length_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID) + .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) + .Where(e => e.Length == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToList_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => o.OrderDetails.ToList()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToList_Contains(bool async) + { + var order = new Order { OrderID = 10248 }; + + return AssertQuery( + async, + ss => ss.Set() + .Select(c => c.Orders.ToList()) + .Where(e => e.Contains(order)), + entryCount: 5); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToArray_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => o.OrderDetails.ToArray()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory(Skip = "Issue#19433")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToArray_Contains(bool async) + { + var order = new Order { OrderID = 10248 }; + + return AssertQuery( + async, + ss => ss.Set() + .Select(c => c.Orders.ToArray()) + .Where(e => e.Contains(order)), + entryCount: 5); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => o.OrderDetails.AsEnumerable()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_AsEnumerable_Contains(bool async) + { + var order = new Order { OrderID = 10248 }; + + return AssertQuery( + async, + ss => ss.Set() + .Select(c => c.Orders.AsEnumerable()) + .Where(e => e.Contains(order)), + entryCount: 5); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToList_Count_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => o.OrderDetails.ToList()) + .Where(e => e.Count == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory(Skip = "Issue#19431")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToArray_Length_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => o.OrderDetails.ToArray()) + .Where(e => e.Length == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } } } diff --git a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs index 2ab142108e1..43ac7ccce6d 100644 --- a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs @@ -476,9 +476,77 @@ public virtual Task Client_method_skip_take_loads_owned_navigations_variation_2( ss => ss.Set().OrderBy(e => e.Id).Select(e => Identity(e)).Skip(1).Take(2)); } - private static OwnedPerson Identity(OwnedPerson person) => person; + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_owned_collection_navigation_ToList_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(p => p.Id) + .Select(p => p.Orders.ToList()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToArray_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(p => p.Id) + .Select(p => p.Orders.ToArray()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(p => p.Id) + .Select(p => p.Orders.AsEnumerable()) + .Where(e => e.Count() == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToList_Count_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(p => p.Id) + .Select(p => p.Orders.ToList()) + .Where(e => e.Count == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory(Skip = "Issue#19431")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_collection_navigation_ToArray_Length_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(p => p.Id) + .Select(p => p.Orders.ToArray()) + .Where(e => e.Length == 0), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + private static OwnedPerson Identity(OwnedPerson person) => person; protected virtual DbContext CreateContext() => Fixture.CreateContext(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 7e94dd15120..c1eeb069199 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -4016,11 +4016,13 @@ public override async Task Correlated_collections_naked_navigation_with_ToList_f await base.Correlated_collections_naked_navigation_with_ToList_followed_by_projecting_count(async); AssertSql( - @"SELECT [g].[Nickname], [g].[SquadId], [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] + @"SELECT ( + SELECT COUNT(*) + FROM [Weapons] AS [w] + WHERE [g].[FullName] = [w].[OwnerFullName]) FROM [Gears] AS [g] -LEFT JOIN [Weapons] AS [w] ON [g].[FullName] = [w].[OwnerFullName] WHERE [g].[Discriminator] IN (N'Gear', N'Officer') AND ([g].[Nickname] <> N'Marcus') -ORDER BY [g].[Nickname], [g].[SquadId], [w].[Id]"); +ORDER BY [g].[Nickname]"); } public override async Task Correlated_collections_naked_navigation_with_ToArray(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index ff6b7e4c1d0..0dd89c4f7d1 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -1362,6 +1362,25 @@ FROM [Employees] AS [e] ORDER BY [e].[EmployeeID]"); } + public override async Task Projection_AsEnumerable_projection(bool async) + { + await base.Projection_AsEnumerable_projection(async); + + AssertSql( + @"SELECT [c].[CustomerID], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10750 +) AS [t] ON [c].[CustomerID] = [t].[CustomerID] +WHERE ([c].[CustomerID] LIKE N'A%') AND (( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE ([o0].[CustomerID] = [c].[CustomerID]) AND ([o0].[OrderID] < 11000)) > 0) +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs index dee4239c6b4..431b8c44d90 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs @@ -1765,6 +1765,236 @@ FROM [Customers] AS [c] WHERE ((CAST(@__i_0 AS nchar(5)) + [c].[CustomerID]) + CAST(@__i_0 AS nchar(5))) = [c].[CompanyName]"); } + public override async Task Where_Queryable_ToList_Count(bool async) + { + await base.Where_Queryable_ToList_Count(async); + + AssertSql( + @"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 ( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID]) = 0 +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_ToList_Contains(bool async) + { + await base.Where_Queryable_ToList_Contains(async); + + AssertSql( + @"SELECT [c].[CustomerID], [o].[CustomerID], [o].[OrderID] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE N'ALFKI' IN ( + SELECT [o0].[CustomerID] + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID] +) + +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_ToArray_Count(bool async) + { + await base.Where_Queryable_ToArray_Count(async); + + AssertSql( + @"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 ( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID]) = 0 +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_ToArray_Contains(bool async) + { + await base.Where_Queryable_ToArray_Contains(async); + + AssertSql( + @"SELECT [c].[CustomerID], [o].[CustomerID], [o].[OrderID] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE N'ALFKI' IN ( + SELECT [o0].[CustomerID] + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID] +) + +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_AsEnumerable_Count(bool async) + { + await base.Where_Queryable_AsEnumerable_Count(async); + + AssertSql( + @"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 ( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID]) = 0 +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_AsEnumerable_Contains(bool async) + { + await base.Where_Queryable_AsEnumerable_Contains(async); + + AssertSql( + @"SELECT [c].[CustomerID], [o].[CustomerID], [o].[OrderID] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE N'ALFKI' IN ( + SELECT [o0].[CustomerID] + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID] +) + +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_ToList_Count_member(bool async) + { + await base.Where_Queryable_ToList_Count_member(async); + + AssertSql( + @"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 ( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE [o0].[CustomerID] = [c].[CustomerID]) = 0 +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_Queryable_ToArray_Length_member(bool async) + { + await base.Where_Queryable_ToArray_Length_member(async); + + AssertSql(" "); + } + + public override async Task Where_collection_navigation_ToList_Count(bool async) + { + await base.Where_collection_navigation_ToList_Count(async); + + AssertSql( + @"SELECT [o].[OrderID], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Orders] AS [o] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE ([o].[OrderID] < 10300) AND (( + SELECT COUNT(*) + FROM [Order Details] AS [o1] + WHERE [o].[OrderID] = [o1].[OrderID]) = 0) +ORDER BY [o].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Where_collection_navigation_ToList_Contains(bool async) + { + await base.Where_collection_navigation_ToList_Contains(async); + + 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 @__entity_equality_order_0_OrderID IN ( + SELECT [o0].[OrderID] + FROM [Orders] AS [o0] + WHERE [c].[CustomerID] = [o0].[CustomerID] +) + +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_collection_navigation_ToArray_Count(bool async) + { + await base.Where_collection_navigation_ToArray_Count(async); + + AssertSql( + @"SELECT [o].[OrderID], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Orders] AS [o] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE ([o].[OrderID] < 10300) AND (( + SELECT COUNT(*) + FROM [Order Details] AS [o1] + WHERE [o].[OrderID] = [o1].[OrderID]) = 0) +ORDER BY [o].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Where_collection_navigation_ToArray_Contains(bool async) + { + await base.Where_collection_navigation_ToArray_Contains(async); + + AssertSql(" "); + } + + public override async Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + await base.Where_collection_navigation_AsEnumerable_Count(async); + + AssertSql( + @"SELECT [o].[OrderID], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Orders] AS [o] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE ([o].[OrderID] < 10300) AND (( + SELECT COUNT(*) + FROM [Order Details] AS [o1] + WHERE [o].[OrderID] = [o1].[OrderID]) = 0) +ORDER BY [o].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Where_collection_navigation_AsEnumerable_Contains(bool async) + { + await base.Where_collection_navigation_AsEnumerable_Contains(async); + + 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 @__entity_equality_order_0_OrderID IN ( + SELECT [o0].[OrderID] + FROM [Orders] AS [o0] + WHERE [c].[CustomerID] = [o0].[CustomerID] +) + +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Where_collection_navigation_ToList_Count_member(bool async) + { + await base.Where_collection_navigation_ToList_Count_member(async); + + AssertSql( + @"SELECT [o].[OrderID], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Orders] AS [o] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE ([o].[OrderID] < 10300) AND (( + SELECT COUNT(*) + FROM [Order Details] AS [o1] + WHERE [o].[OrderID] = [o1].[OrderID]) = 0) +ORDER BY [o].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Where_collection_navigation_ToArray_Length_member(bool async) + { + await base.Where_collection_navigation_ToArray_Length_member(async); + + AssertSql(" "); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs index ca24011c036..c1d71192a59 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs @@ -2366,6 +2366,73 @@ WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); } + public override async Task Where_owned_collection_navigation_ToList_Count(bool async) + { + await base.Where_owned_collection_navigation_ToList_Count(async); + + AssertSql( + @"SELECT [o].[Id], [o0].[ClientId], [o0].[Id] +FROM [OwnedPerson] AS [o] +LEFT JOIN [Order] AS [o0] ON [o].[Id] = [o0].[ClientId] +WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') AND (( + SELECT COUNT(*) + FROM [Order] AS [o1] + WHERE [o].[Id] = [o1].[ClientId]) = 0) +ORDER BY [o].[Id], [o0].[ClientId], [o0].[Id]"); + } + + public override async Task Where_collection_navigation_ToArray_Count(bool async) + { + await base.Where_collection_navigation_ToArray_Count(async); + + AssertSql( + @"SELECT [o].[Id], [o0].[ClientId], [o0].[Id] +FROM [OwnedPerson] AS [o] +LEFT JOIN [Order] AS [o0] ON ([o].[Id] = [o0].[ClientId]) AND ([o].[Id] = [o0].[ClientId]) +WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') AND (( + SELECT COUNT(*) + FROM [Order] AS [o1] + WHERE [o].[Id] = [o1].[ClientId]) = 0) +ORDER BY [o].[Id], [o0].[ClientId], [o0].[Id]"); + } + + public override async Task Where_collection_navigation_AsEnumerable_Count(bool async) + { + await base.Where_collection_navigation_AsEnumerable_Count(async); + + AssertSql( + @"SELECT [o].[Id], [o0].[ClientId], [o0].[Id] +FROM [OwnedPerson] AS [o] +LEFT JOIN [Order] AS [o0] ON [o].[Id] = [o0].[ClientId] +WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') AND (( + SELECT COUNT(*) + FROM [Order] AS [o1] + WHERE [o].[Id] = [o1].[ClientId]) = 0) +ORDER BY [o].[Id], [o0].[ClientId], [o0].[Id]"); + } + + public override async Task Where_collection_navigation_ToList_Count_member(bool async) + { + await base.Where_collection_navigation_ToList_Count_member(async); + + AssertSql( + @"SELECT [o].[Id], [o0].[ClientId], [o0].[Id] +FROM [OwnedPerson] AS [o] +LEFT JOIN [Order] AS [o0] ON [o].[Id] = [o0].[ClientId] +WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') AND (( + SELECT COUNT(*) + FROM [Order] AS [o1] + WHERE [o].[Id] = [o1].[ClientId]) = 0) +ORDER BY [o].[Id], [o0].[ClientId], [o0].[Id]"); + } + + public override async Task Where_collection_navigation_ToArray_Length_member(bool async) + { + await base.Where_collection_navigation_ToArray_Length_member(async); + + AssertSql(" "); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);