diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 7894cc19e0e..f82fce3fc1f 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Internal; namespace Microsoft.EntityFrameworkCore.Query.Internal; @@ -1070,6 +1071,11 @@ private NavigationExpansionExpression ProcessInclude( if (currentExpression is MethodCallExpression methodCallExpression) { + if (methodCallExpression.Method.IsEFPropertyMethod()) + { + return (currentExpression, default); + } + if (!methodCallExpression.Method.IsGenericMethod || !SupportedFilteredIncludeOperations.Contains(methodCallExpression.Method.GetGenericMethodDefinition())) { @@ -2037,41 +2043,18 @@ private IncludeTreeNode PopulateIncludeTree(IncludeTreeNode includeTreeNode, Exp case ParameterExpression: return includeTreeNode; - case MemberExpression memberExpression - when memberExpression.Expression != null: - var innerExpression = memberExpression.Expression.UnwrapTypeConversion(out var convertedType); - var innerIncludeTreeNode = PopulateIncludeTree(includeTreeNode, innerExpression, setLoaded); - var entityType = innerIncludeTreeNode.EntityType; - if (convertedType != null) + case MethodCallExpression methodCallExpression + when methodCallExpression.TryGetEFPropertyArguments(out var entityExpression, out var propertyName): + if (TryExtractIncludeTreeNode(entityExpression, propertyName, out var addedEfPropertyNode)) { - entityType = entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) - .FirstOrDefault(et => et.ClrType == convertedType); - if (entityType == null) - { - throw new InvalidOperationException( - CoreStrings.InvalidTypeConversationWithInclude(expression, convertedType.ShortDisplayName())); - } + return addedEfPropertyNode; } - var navigation = entityType.FindNavigation(memberExpression.Member); - if (navigation != null) - { - var addedNode = innerIncludeTreeNode.AddNavigation(navigation, setLoaded); - - // This is to add eager Loaded navigations when owner type is included. - PopulateEagerLoadedNavigations(addedNode); - - return addedNode; - } + break; - var skipNavigation = entityType.FindSkipNavigation(memberExpression.Member); - if (skipNavigation != null) + case MemberExpression { Expression: { } } memberExpression: + if (TryExtractIncludeTreeNode(memberExpression.Expression, memberExpression.Member.Name, out var addedNode)) { - var addedNode = innerIncludeTreeNode.AddNavigation(skipNavigation, setLoaded); - - // This is to add eager Loaded navigations when owner type is included. - PopulateEagerLoadedNavigations(addedNode); - return addedNode; } @@ -2079,6 +2062,43 @@ private IncludeTreeNode PopulateIncludeTree(IncludeTreeNode includeTreeNode, Exp } throw new InvalidOperationException(CoreStrings.InvalidIncludeExpression(expression)); + + bool TryExtractIncludeTreeNode( + Expression innerExpression, + string propertyName, + [NotNullWhen(true)] out IncludeTreeNode? addedNode) + { + innerExpression = innerExpression.UnwrapTypeConversion(out var convertedType); + var innerIncludeTreeNode = PopulateIncludeTree(includeTreeNode, innerExpression, setLoaded); + var entityType = innerIncludeTreeNode.EntityType; + + if (convertedType != null) + { + entityType = entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) + .FirstOrDefault(et => et.ClrType == convertedType); + if (entityType == null) + { + throw new InvalidOperationException( + CoreStrings.InvalidTypeConversationWithInclude(expression, convertedType.ShortDisplayName())); + } + } + + var navigation = (INavigationBase?)entityType.FindNavigation(propertyName) + ?? entityType.FindSkipNavigation(propertyName); + + if (navigation != null) + { + addedNode = innerIncludeTreeNode.AddNavigation(navigation, setLoaded); + + // This is to add eager Loaded navigations when owner type is included. + PopulateEagerLoadedNavigations(addedNode); + + return true; + } + + addedNode = null; + return false; + } } private Expression Reduce(Expression source) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs index 6fe0155f2f4..6b7b5e12452 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs @@ -4146,6 +4146,13 @@ public override async Task String_include_on_incorrect_property_throws(bool asyn AssertSql(); } + public override async Task EF_Property_include_on_incorrect_property_throws(bool async) + { + await base.EF_Property_include_on_incorrect_property_throws(async); + + AssertSql(); + } + public override async Task SkipWhile_throws_meaningful_exception(bool async) { await base.SkipWhile_throws_meaningful_exception(async); diff --git a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryInMemoryTest.cs new file mode 100644 index 00000000000..669509ada06 --- /dev/null +++ b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryInMemoryTest.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query; + +public class NorthwindEFPropertyIncludeQueryInMemoryTest : NorthwindEFPropertyIncludeQueryTestBase< + NorthwindQueryInMemoryFixture> +{ + public NorthwindEFPropertyIncludeQueryInMemoryTest( + NorthwindQueryInMemoryFixture fixture, + ITestOutputHelper testOutputHelper) + : base(fixture) + { + //TestLoggerFactory.TestOutputHelper = testOutputHelper; + } +} diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsQueryTestBase.cs index 0205852d3cc..b6e38da3a54 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsQueryTestBase.cs @@ -612,6 +612,23 @@ public virtual Task Multiple_complex_includes(bool async) new ExpectedInclude(l1 => l1.OneToMany_Optional1), new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2, "OneToMany_Optional1"))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Multiple_complex_includes_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Include(e => EF.Property(e, "OneToOne_Optional_FK1")) + .ThenInclude(e => EF.Property>(e, "OneToMany_Optional2")) + .Include(e => EF.Property>(e, "OneToMany_Optional1")) + .ThenInclude(e => EF.Property(e, "OneToOne_Optional_FK2")), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedInclude(l1 => l1.OneToOne_Optional_FK1), + new ExpectedInclude(l2 => l2.OneToMany_Optional2, "OneToOne_Optional_FK1"), + new ExpectedInclude(l1 => l1.OneToMany_Optional1), + new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2, "OneToMany_Optional1"))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Multiple_complex_includes_self_ref(bool async) @@ -628,6 +645,22 @@ public virtual Task Multiple_complex_includes_self_ref(bool async) new ExpectedInclude(l1 => l1.OneToMany_Optional_Self1), new ExpectedInclude(l2 => l2.OneToOne_Optional_Self1, "OneToMany_Optional_Self1"))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Multiple_complex_includes_self_ref_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Include(e => EF.Property(e, "OneToOne_Optional_Self1")) + .ThenInclude(e => EF.Property>(e, "OneToMany_Optional_Self1")) + .Include(e => EF.Property>(e, "OneToMany_Optional_Self1")) + .ThenInclude(e => EF.Property(e, "OneToOne_Optional_Self1")), + elementAsserter: (e, a) => AssertInclude( + e, a, new ExpectedInclude(l1 => l1.OneToOne_Optional_Self1), + new ExpectedInclude(l2 => l2.OneToMany_Optional_Self1, "OneToOne_Optional_Self1"), + new ExpectedInclude(l1 => l1.OneToMany_Optional_Self1), + new ExpectedInclude(l2 => l2.OneToOne_Optional_Self1, "OneToMany_Optional_Self1"))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Include_reference_and_collection_order_by(bool async) @@ -1310,6 +1343,21 @@ public virtual Task Include_collection_multiple_with_filter(bool async) new ExpectedInclude(e2 => e2.OneToOne_Optional_PK2, "OneToMany_Optional1"), new ExpectedInclude(e3 => e3.OneToOne_Optional_FK3, "OneToMany_Optional1.OneToOne_Optional_PK2"))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Include_collection_multiple_with_filter_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Include(l1 => EF.Property>(l1, "OneToMany_Optional1")) + .ThenInclude(l2 => EF.Property(l2, "OneToOne_Optional_PK2")) + .ThenInclude(l3 => EF.Property(l3, "OneToOne_Optional_FK3")) + .Where(l1 => EF.Property>(l1, "OneToMany_Optional1").Where(l2 => l2.OneToOne_Optional_PK2.Name != "Foo").Count() > 0), + elementAsserter: (e, a) => AssertInclude( + e, a, new ExpectedInclude(e1 => e1.OneToMany_Optional1), + new ExpectedInclude(e2 => e2.OneToOne_Optional_PK2, "OneToMany_Optional1"), + new ExpectedInclude(e3 => e3.OneToOne_Optional_FK3, "OneToMany_Optional1.OneToOne_Optional_PK2"))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Including_reference_navigation_and_projecting_collection_navigation(bool async) @@ -1353,6 +1401,18 @@ public virtual Task Filtered_include_basic_Where(bool async) e => e.OneToMany_Optional1, includeFilter: x => x.Where(l2 => l2.Id > 5)))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_basic_Where_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(l1 => EF.Property>(l1, "OneToMany_Optional1").Where(l2 => l2.Id > 5)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedFilteredInclude( + e => e.OneToMany_Optional1, + includeFilter: x => x.Where(l2 => l2.Id > 5)))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_include_OrderBy(bool async) @@ -1366,6 +1426,19 @@ public virtual Task Filtered_include_OrderBy(bool async) includeFilter: x => x.OrderBy(x => x.Name), assertOrder: true))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_OrderBy_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(l1 => EF.Property>(l1, "OneToMany_Optional1").OrderBy(x => x.Name)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedFilteredInclude( + e => e.OneToMany_Optional1, + includeFilter: x => x.OrderBy(x => x.Name), + assertOrder: true))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_ThenInclude_OrderBy(bool async) @@ -1440,6 +1513,20 @@ public virtual Task Filtered_include_basic_OrderBy_Skip_Take(bool async) includeFilter: x => x.OrderBy(x => x.Name).Skip(1).Take(3), assertOrder: true))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_basic_OrderBy_Skip_Take_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(l1 => EF.Property>(l1, "OneToMany_Optional1") + .OrderBy(x => x.Name).Skip(1).Take(3)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedFilteredInclude( + e => e.OneToMany_Optional1, + includeFilter: x => x.OrderBy(x => x.Name).Skip(1).Take(3), + assertOrder: true))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_include_Skip_without_OrderBy(bool async) @@ -1483,6 +1570,24 @@ public virtual Task Filtered_include_on_ThenInclude(bool async) x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3), assertOrder: true))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_on_ThenInclude_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Include(l1 => EF.Property(l1, "OneToOne_Optional_FK1")) + .ThenInclude(l2 => EF.Property>(l2, "OneToMany_Optional2") + .Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedInclude(e => e.OneToOne_Optional_FK1), + new ExpectedFilteredInclude( + e => e.OneToMany_Optional2, + "OneToOne_Optional_FK1", + x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3), + assertOrder: true))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_include_after_reference_navigation(bool async) diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsSharedTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsSharedTypeQueryTestBase.cs index 0bbe278b0a9..7032a52783a 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsSharedTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsSharedTypeQueryTestBase.cs @@ -18,6 +18,12 @@ public override async Task Multiple_complex_includes_self_ref(bool async) (await Assert.ThrowsAsync( () => base.Multiple_complex_includes_self_ref(async))).Message); + public override async Task Multiple_complex_includes_self_ref_EF_Property(bool async) + => Assert.Equal( + CoreStrings.InvalidIncludeExpression("Property(e, \"OneToOne_Optional_Self1\")"), + (await Assert.ThrowsAsync( + () => base.Multiple_complex_includes_self_ref_EF_Property(async))).Message); + public override Task Complex_SelectMany_with_nested_navigations_and_explicit_DefaultIfEmpty_with_other_query_operators_composed_on_top(bool async) => AssertTranslationFailed( diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index 5fcbdda300d..21e5fc23f29 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -1270,6 +1270,16 @@ public virtual Task SelectMany_with_string_based_Include1(bool async) .Include("OneToOne_Required_FK2"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Required_FK2))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_with_EF_Property_Include1(bool async) + => AssertQuery( + async, + ss => ss.Set() + .SelectMany(l1 => l1.OneToMany_Optional1) + .Include(l2 => EF.Property(l2, "OneToOne_Required_FK2")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Required_FK2))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task SelectMany_with_string_based_Include2(bool async) @@ -1293,6 +1303,17 @@ public virtual Task Multiple_SelectMany_with_string_based_Include(bool async) .Include("OneToOne_Required_FK3"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l3 => l3.OneToOne_Required_FK3))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Multiple_SelectMany_with_EF_Property_Include(bool async) + => AssertQuery( + async, + ss => ss.Set() + .SelectMany(l1 => l1.OneToMany_Optional1) + .SelectMany(l1 => l1.OneToMany_Optional2) + .Include(l3 => EF.Property(l3, "OneToOne_Required_FK3")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l3 => l3.OneToOne_Required_FK3))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Multiple_required_navigations_with_Include(bool async) @@ -1330,6 +1351,19 @@ public virtual Task Multiple_required_navigation_with_string_based_Include(bool .Include("OneToOne_Optional_FK2"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2)))); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Multiple_required_navigation_with_EF_Property_Include(bool async) + // Include after select. Issue #16752. + => AssertIncludeOnNonEntity( + () => AssertQuery( + async, + ss => ss.Set() + .Select(l4 => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3) + .Include(l2 => EF.Property(l2, "OneToOne_Optional_FK2")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2)))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Multiple_required_navigation_using_multiple_selects_with_string_based_Include(bool async) @@ -1343,6 +1377,19 @@ public virtual Task Multiple_required_navigation_using_multiple_selects_with_str .Include("OneToOne_Optional_FK2"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2)))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Multiple_required_navigation_using_multiple_selects_with_EF_Property_Include(bool async) + // Include after select. Issue #16752. + => AssertIncludeOnNonEntity( + () => AssertQuery( + async, + ss => ss.Set() + .Select(l4 => l4.OneToOne_Required_FK_Inverse4) + .Select(l3 => l3.OneToOne_Required_FK_Inverse3) + .Include(l2 => EF.Property(l2, "OneToOne_Optional_FK2")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(l2 => l2.OneToOne_Optional_FK2)))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Optional_navigation_with_Include(bool async) diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 1118d6e2d7e..48ca2e69ce2 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -197,6 +197,14 @@ public virtual Task String_based_Include_navigation_on_derived_type(bool async) ss => ss.Set().OfType().Include("Reports"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(o => o.Reports))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task EF_Property_based_Include_navigation_on_derived_type(bool async) + => AssertQuery( + async, + ss => ss.Set().OfType().Include(o => EF.Property(o, "Reports")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(o => o.Reports))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_Where_Navigation_Included(bool async) @@ -2930,6 +2938,14 @@ public virtual Task Include_reference_on_derived_type_using_string(bool async) ss => ss.Set().Include("DefeatedBy"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(lc => lc.DefeatedBy))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Include_reference_on_derived_type_using_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(lc => EF.Property((LocustCommander)lc, "DefeatedBy")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(lc => lc.DefeatedBy))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Include_reference_on_derived_type_using_string_nested1(bool async) @@ -2986,6 +3002,14 @@ public virtual Task Include_collection_on_derived_type_using_string(bool async) ss => ss.Set().Include("Reports"), elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(o => o.Reports))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Include_collection_on_derived_type_using_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(o => EF.Property>((Officer)o, "Reports")), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(o => o.Reports))); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Include_collection_on_derived_type_using_lambda(bool async) diff --git a/test/EFCore.Specification.Tests/Query/IncludeOneToOneTestBase.cs b/test/EFCore.Specification.Tests/Query/IncludeOneToOneTestBase.cs index add7686d2db..953af26f068 100644 --- a/test/EFCore.Specification.Tests/Query/IncludeOneToOneTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/IncludeOneToOneTestBase.cs @@ -31,6 +31,20 @@ var people Assert.Equal(4 + 3, context.ChangeTracker.Entries().Count()); } + [ConditionalFact] + public virtual void Include_address_EF_Property() + { + using var context = CreateContext(); + var people + = context.Set() + .Include(p => EF.Property(p, "Address")) + .ToList(); + + Assert.Equal(4, people.Count); + Assert.Equal(3, people.Count(p => p.Address != null)); + Assert.Equal(4 + 3, context.ChangeTracker.Entries().Count()); + } + [ConditionalFact] public virtual void Include_address_shadow() { @@ -60,6 +74,21 @@ var people Assert.Empty(context.ChangeTracker.Entries()); } + [ConditionalFact] + public virtual void Include_address_no_tracking_EF_Property() + { + using var context = CreateContext(); + var people + = context.Set() + .Include(p => EF.Property(p, "Address")) + .AsNoTracking() + .ToList(); + + Assert.Equal(4, people.Count); + Assert.Equal(3, people.Count(p => p.Address != null)); + Assert.Empty(context.ChangeTracker.Entries()); + } + [ConditionalFact] public virtual void Include_person() { @@ -74,6 +103,20 @@ var addresses Assert.Equal(3 + 3, context.ChangeTracker.Entries().Count()); } + [ConditionalFact] + public virtual void Include_person_EF_Property() + { + using var context = CreateContext(); + var addresses + = context.Set
() + .Include(a => EF.Property
(a, "Resident")) + .ToList(); + + Assert.Equal(3, addresses.Count); + Assert.True(addresses.All(p => p.Resident != null)); + Assert.Equal(3 + 3, context.ChangeTracker.Entries().Count()); + } + [ConditionalFact] public virtual void Include_person_shadow() { @@ -103,6 +146,21 @@ var addresses Assert.Empty(context.ChangeTracker.Entries()); } + [ConditionalFact] + public virtual void Include_person_no_tracking_EF_Property() + { + using var context = CreateContext(); + var addresses + = context.Set
() + .Include(a => EF.Property
(a, "Resident")) + .AsNoTracking() + .ToList(); + + Assert.Equal(3, addresses.Count); + Assert.True(addresses.All(p => p.Resident != null)); + Assert.Empty(context.ChangeTracker.Entries()); + } + [ConditionalFact] public virtual void Include_address_when_person_already_tracked() { @@ -146,7 +204,8 @@ protected virtual DbContext CreateContext() public abstract class OneToOneQueryFixtureBase : SharedStoreFixtureBase { - protected override string StoreName { get; } = "OneToOneQueryTest"; + protected override string StoreName + => "OneToOneQueryTest"; protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { diff --git a/test/EFCore.Specification.Tests/Query/ManyToManyQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ManyToManyQueryTestBase.cs index 9e14fa2c1c9..266a94ace57 100644 --- a/test/EFCore.Specification.Tests/Query/ManyToManyQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ManyToManyQueryTestBase.cs @@ -470,6 +470,19 @@ public virtual Task Filtered_include_skip_navigation_order_by_take(bool async) et => et.TwoSkipShared, includeFilter: x => x.OrderBy(i => i.Id).Take(2))), entryCount: 63); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_skip_navigation_order_by_take_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include( + e => EF.Property>(e, "TwoSkipShared").OrderBy(i => i.Id).Take(2)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedFilteredInclude( + et => et.TwoSkipShared, includeFilter: x => x.OrderBy(i => i.Id).Take(2))), + entryCount: 63); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_include_skip_navigation_order_by_skip_take(bool async) @@ -538,6 +551,22 @@ public virtual Task Filtered_include_skip_navigation_order_by_skip_take_then_inc et => et.ThreeSkipFull, "TwoSkip", includeFilter: x => x.Where(i => i.Id < 10))), entryCount: 100); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(e => EF.Property>(e, "TwoSkip").OrderBy(i => i.Id).Skip(1).Take(2)) + .ThenInclude>( + e => EF.Property>(e, "ThreeSkipFull").Where(i => i.Id < 10)), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedFilteredInclude( + et => et.TwoSkip, includeFilter: x => x.OrderBy(i => i.Id).Skip(1).Take(2)), + new ExpectedFilteredInclude( + et => et.ThreeSkipFull, "TwoSkip", includeFilter: x => x.Where(i => i.Id < 10))), + entryCount: 100); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Filtered_include_skip_navigation_where_then_include_skip_navigation_order_by_skip_take(bool async) @@ -665,7 +694,7 @@ public virtual Task Includes_accessed_via_different_path_are_merged(bool async) [ConditionalTheory(Skip = "Issue#21332")] [MemberData(nameof(IsAsyncData))] - public virtual Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public virtual Task Filtered_includes_accessed_via_different_path_are_merged(bool async) => AssertQuery( async, ss => ss.Set().Include(e => e.OneSkipPayloadFull).ThenInclude(e => e.Collection.Where(i => i.Id < 5)) diff --git a/test/EFCore.Specification.Tests/Query/NorthwindEFPropertyIncludeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindEFPropertyIncludeQueryTestBase.cs new file mode 100644 index 00000000000..4d44c606a24 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/NorthwindEFPropertyIncludeQueryTestBase.cs @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.ObjectModel; +using Microsoft.EntityFrameworkCore.Diagnostics.Internal; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.TestModels.Northwind; + +namespace Microsoft.EntityFrameworkCore.Query; + +public abstract class NorthwindEFPropertyIncludeQueryTestBase : NorthwindIncludeQueryTestBase + where TFixture : NorthwindQueryFixtureBase, new() +{ + private static readonly IncludeRewritingExpressionVisitor _includeRewritingExpressionVisitor = new(); + + protected NorthwindEFPropertyIncludeQueryTestBase(TFixture fixture) + : base(fixture) + { + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Include_non_existing_navigation(bool async) + => Assert.Contains( + CoreStrings.InvalidIncludeExpression("Property(o, \"ArcticMonkeys\")"), + (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set().Include(o => EF.Property(o, "ArcticMonkeys"))))).Message); + + public override async Task Include_property(bool async) + => Assert.Contains( + CoreStrings.InvalidIncludeExpression("Property(o, \"OrderDate\")"), + (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set().Include(o => o.OrderDate)))).Message); + + public override async Task Include_closes_reader(bool async) + { + using var context = CreateContext(); + if (async) + { + Assert.NotNull(await context.Set().Include(c => EF.Property(c, "Orders")).FirstOrDefaultAsync()); + Assert.NotNull(await context.Set().ToListAsync()); + } + else + { + Assert.NotNull(context.Set().Include(c => EF.Property(c, "Orders")).FirstOrDefault()); + Assert.NotNull(context.Set().ToList()); + } + } + + public override async Task Include_collection_dependent_already_tracked(bool async) + { + using var context = CreateContext(); + var orders = context.Set().Where(o => o.CustomerID == "ALFKI").ToList(); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); + + var customer + = async + ? await context.Set() + .Include(c => EF.Property(c, "Orders")) + .SingleAsync(c => c.CustomerID == "ALFKI") + : context.Set() + .Include(c => EF.Property(c, "Orders")) + .Single(c => c.CustomerID == "ALFKI"); + + Assert.Equal(orders, customer.Orders, LegacyReferenceEqualityComparer.Instance); + Assert.Equal(6, customer.Orders.Count); + Assert.True(orders.All(o => ReferenceEquals(o.Customer, customer))); + Assert.Equal(6 + 1, context.ChangeTracker.Entries().Count()); + } + + public override async Task Include_collection_principal_already_tracked(bool async) + { + using var context = CreateContext(); + var customer1 = context.Set().Single(c => c.CustomerID == "ALFKI"); + Assert.Single(context.ChangeTracker.Entries()); + + var customer2 + = async + ? await context.Set() + .Include(c => EF.Property(c, "Orders")) + .SingleAsync(c => c.CustomerID == "ALFKI") + : context.Set() + .Include(c => EF.Property(c, "Orders")) + .Single(c => c.CustomerID == "ALFKI"); + + Assert.Same(customer1, customer2); + Assert.Equal(6, customer2.Orders.Count); + Assert.True(customer2.Orders.All(o => o.Customer != null)); + Assert.Equal(7, context.ChangeTracker.Entries().Count()); + } + + public override async Task Include_reference_dependent_already_tracked(bool async) + { + using var context = CreateContext(); + var customer = context.Set().Single(o => o.CustomerID == "ALFKI"); + Assert.Single(context.ChangeTracker.Entries()); + + var orders + = async + ? await context.Set().Include(o => EF.Property(o, "Customer")).Where(o => o.CustomerID == "ALFKI").ToListAsync() + : context.Set().Include(o => EF.Property(o, "Customer")).Where(o => o.CustomerID == "ALFKI").ToList(); + + Assert.Equal(6, orders.Count); + Assert.True(orders.All(o => ReferenceEquals(o.Customer, customer))); + Assert.Equal(7, context.ChangeTracker.Entries().Count()); + } + + public override async Task Include_specified_on_non_entity_not_supported(bool async) + => Assert.Equal( + CoreStrings.IncludeOnNonEntity("t => t.Item1.Orders"), + (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set().Select(c => new Tuple(c, 5)).Include(t => t.Item1.Orders)))).Message); + + protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression) + { + serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression); + + return _includeRewritingExpressionVisitor.Visit(serverQueryExpression); + } + + private class IncludeRewritingExpressionVisitor : ExpressionVisitor + { + // ReSharper disable StaticMemberInGenericType + private static readonly MethodInfo _includeMethodInfo + = typeof(EntityFrameworkQueryableExtensions) + .GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.Include)) + .Single( + mi => + mi.GetGenericArguments().Count() == 2 + && mi.GetParameters().Any( + pi => pi.Name == "navigationPropertyPath" && pi.ParameterType != typeof(string))); + + private static readonly MethodInfo _thenIncludeAfterReferenceMethodInfo + = typeof(EntityFrameworkQueryableExtensions) + .GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.ThenInclude)) + .Single( + mi => mi.GetGenericArguments().Count() == 3 + && mi.GetParameters()[0].ParameterType.GenericTypeArguments[1].IsGenericParameter); + + private static readonly MethodInfo _thenIncludeAfterEnumerableMethodInfo + = typeof(EntityFrameworkQueryableExtensions) + .GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.ThenInclude)) + .Where(mi => mi.GetGenericArguments().Count() == 3) + .Single( + mi => + { + var typeInfo = mi.GetParameters()[0].ParameterType.GenericTypeArguments[1]; + return typeInfo.IsGenericType + && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>); + }); + + private static readonly MethodInfo _propertyMethod + = typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(EF.Property))!; + + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + if (methodCallExpression.Method.DeclaringType == typeof(EntityFrameworkQueryableExtensions) + && methodCallExpression.Method.IsGenericMethod) + { + var genericMethodDefinition = methodCallExpression.Method.GetGenericMethodDefinition(); + if (genericMethodDefinition == _includeMethodInfo) + { + return BuildEFPropertyCallExpression(0, 1); + } + + if (genericMethodDefinition == _thenIncludeAfterEnumerableMethodInfo + || genericMethodDefinition == _thenIncludeAfterReferenceMethodInfo) + { + return BuildEFPropertyCallExpression(1, 2); + } + } + + return base.VisitMethodCall(methodCallExpression); + + Expression BuildEFPropertyCallExpression(int entityTypeIndex, int propertyTypeIndex) + { + var arguments = methodCallExpression.Arguments; + var genericArguments = methodCallExpression.Method.GetGenericArguments(); + var fromQuote = arguments[1].UnwrapLambdaFromQuote(); + var parameterExpression = fromQuote.Parameters[0]; + + var path = GetPath(fromQuote.Body); + if (path != null && !path.Contains(".")) + { + return Expression.Call( + methodCallExpression.Method, + Visit(arguments[0]), + Expression.Quote( + Expression.Lambda( + typeof(Func<,>).MakeGenericType(genericArguments[entityTypeIndex], genericArguments[propertyTypeIndex]), + Expression.Call( + _propertyMethod.MakeGenericMethod(genericArguments[propertyTypeIndex]), + parameterExpression, + Expression.Constant(path)), + parameterExpression))); + } + + return base.VisitMethodCall(methodCallExpression); + } + } + + private static string GetPath(Expression expression) + { + switch (expression) + { + case MemberExpression memberExpression: + if (memberExpression.Expression is ParameterExpression) + { + return memberExpression.Member.Name; + } + + return $"{GetPath(memberExpression.Expression)}.{memberExpression.Member.Name}"; + + case UnaryExpression unaryExpression + when unaryExpression.NodeType == ExpressionType.Convert + || unaryExpression.NodeType == ExpressionType.Convert + || unaryExpression.NodeType == ExpressionType.TypeAs: + return GetPath(unaryExpression.Operand); + + default: + return null; + } + } + } +} diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs index b9ec1575985..2069a2dadf4 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -5277,6 +5277,13 @@ public virtual Task String_include_on_incorrect_property_throws(bool async) => Assert.ThrowsAsync( async () => await AssertQuery(async, ss => ss.Set().Include("OrderDetails"))); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task EF_Property_include_on_incorrect_property_throws(bool async) + => Assert.ThrowsAsync( + async () => await AssertQuery(async, ss => ss.Set().Include(c => EF.Property(c, "OrderDetails")))); + [ConditionalTheory] [InlineData(false, false)] [InlineData(false, true)] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs index dd8a3675305..2f62ff199f7 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -2446,6 +2446,125 @@ GROUP BY [l2].[Name] ORDER BY [l].[Id], [l0].[Id], [l1].[Id]"); } + public override async Task Multiple_complex_includes_EF_Property(bool async) + { + await base.Multiple_complex_includes_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [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], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [l3].[Id] AS [Id0], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id] + FROM [LevelTwo] AS [l2] + LEFT JOIN [LevelThree] AS [l3] ON [l2].[Id] = [l3].[Level2_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Id], [l1].[Id], [t].[Id]"); + } + + public override async Task Multiple_complex_includes_self_ref_EF_Property(bool async) + { + await base.Multiple_complex_includes_self_ref_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Name], [l0].[OneToMany_Optional_Self_Inverse1Id], [l0].[OneToMany_Required_Self_Inverse1Id], [l0].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_Inverse1Id], [l1].[OneToMany_Required_Self_Inverse1Id], [l1].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [t].[Id0], [t].[Date0], [t].[Name0], [t].[OneToMany_Optional_Self_Inverse1Id0], [t].[OneToMany_Required_Self_Inverse1Id0], [t].[OneToOne_Optional_Self1Id0] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelOne] AS [l0] ON [l].[OneToOne_Optional_Self1Id] = [l0].[Id] +LEFT JOIN [LevelOne] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Self_Inverse1Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Name], [l2].[OneToMany_Optional_Self_Inverse1Id], [l2].[OneToMany_Required_Self_Inverse1Id], [l2].[OneToOne_Optional_Self1Id], [l3].[Id] AS [Id0], [l3].[Date] AS [Date0], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Self_Inverse1Id] AS [OneToMany_Optional_Self_Inverse1Id0], [l3].[OneToMany_Required_Self_Inverse1Id] AS [OneToMany_Required_Self_Inverse1Id0], [l3].[OneToOne_Optional_Self1Id] AS [OneToOne_Optional_Self1Id0] + FROM [LevelOne] AS [l2] + LEFT JOIN [LevelOne] AS [l3] ON [l2].[OneToOne_Optional_Self1Id] = [l3].[Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Self_Inverse1Id] +ORDER BY [l].[Id], [l0].[Id], [l1].[Id], [t].[Id]"); + } + + public override async Task Include_collection_multiple_with_filter_EF_Property(bool async) + { + await base.Include_collection_multiple_with_filter_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t].[Id1], [t].[Level3_Optional_Id], [t].[Level3_Required_Id], [t].[Name1], [t].[OneToMany_Optional_Inverse4Id], [t].[OneToMany_Optional_Self_Inverse4Id], [t].[OneToMany_Required_Inverse4Id], [t].[OneToMany_Required_Self_Inverse4Id], [t].[OneToOne_Optional_PK_Inverse4Id], [t].[OneToOne_Optional_Self4Id] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [l3].[Id] AS [Id0], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id], [l4].[Id] AS [Id1], [l4].[Level3_Optional_Id], [l4].[Level3_Required_Id], [l4].[Name] AS [Name1], [l4].[OneToMany_Optional_Inverse4Id], [l4].[OneToMany_Optional_Self_Inverse4Id], [l4].[OneToMany_Required_Inverse4Id], [l4].[OneToMany_Required_Self_Inverse4Id], [l4].[OneToOne_Optional_PK_Inverse4Id], [l4].[OneToOne_Optional_Self4Id] + FROM [LevelTwo] AS [l2] + LEFT JOIN [LevelThree] AS [l3] ON [l2].[Id] = [l3].[OneToOne_Optional_PK_Inverse3Id] + LEFT JOIN [LevelFour] AS [l4] ON [l3].[Id] = [l4].[Level3_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +WHERE ( + SELECT COUNT(*) + FROM [LevelTwo] AS [l0] + LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToOne_Optional_PK_Inverse3Id] + WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] AND ([l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL)) > 0 +ORDER BY [l].[Id], [t].[Id], [t].[Id0]"); + } + + public override async Task Filtered_include_basic_Where_EF_Property(bool async) + { + await base.Filtered_include_basic_Where_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] > 5 +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id]"); + } + + public override async Task Filtered_include_OrderBy_EF_Property(bool async) + { + await base.Filtered_include_OrderBy_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Name]"); + } + + public override async Task Filtered_include_basic_OrderBy_Skip_Take_EF_Property(bool async) + { + await base.Filtered_include_basic_OrderBy_Skip_Take_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id] + FROM ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Name]) AS [row] + FROM [LevelTwo] AS [l0] + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t0].[OneToMany_Optional_Inverse2Id], [t0].[Name]"); + } + + public override async Task Filtered_include_on_ThenInclude_EF_Property(bool async) + { + await base.Filtered_include_on_ThenInclude_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id] + FROM ( + SELECT [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], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Optional_Inverse3Id] ORDER BY [l1].[Name]) AS [row] + FROM [LevelThree] AS [l1] + WHERE [l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l0].[Id] = [t0].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [l0].[Id], [t0].[OneToMany_Optional_Inverse3Id], [t0].[Name]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs index 3cdbd4ef575..8bb0537cb63 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs @@ -3291,6 +3291,162 @@ GROUP BY [l2].[Level3_Name] ORDER BY [l].[Id], [t].[Id], [t0].[Id]"); } + public override async Task Multiple_complex_includes_self_ref_EF_Property(bool async) + { + await base.Multiple_complex_includes_self_ref_EF_Property(async); + + AssertSql(); + } + + public override async Task Multiple_complex_includes_EF_Property(bool async) + { + await base.Multiple_complex_includes_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Level3_Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t1].[Id], [t1].[OneToOne_Required_PK_Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Level2_Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Level3_Name], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Level3_Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l1] + WHERE [l1].[Level2_Required_Id] IS NOT NULL AND [l1].[OneToMany_Required_Inverse3Id] IS NOT NULL +) AS [t0] ON CASE + WHEN [t].[OneToOne_Required_PK_Date] IS NOT NULL AND [t].[Level1_Required_Id] IS NOT NULL AND [t].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [t].[Id] +END = [t0].[OneToMany_Optional_Inverse3Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[OneToOne_Required_PK_Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Level2_Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [t2].[Id] AS [Id0], [t2].[Level2_Optional_Id], [t2].[Level2_Required_Id], [t2].[Level3_Name], [t2].[OneToMany_Optional_Inverse3Id], [t2].[OneToMany_Required_Inverse3Id], [t2].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l3] + WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse3Id] IS NOT NULL + ) AS [t2] ON CASE + WHEN [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l2].[Id] + END = [t2].[Level2_Optional_Id] + WHERE [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t1] ON [l].[Id] = [t1].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t].[Id], [t0].[Id], [t1].[Id]"); + } + + public override async Task Include_collection_multiple_with_filter_EF_Property(bool async) + { + await base.Include_collection_multiple_with_filter_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t1].[Id], [t1].[OneToOne_Required_PK_Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Level2_Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Level3_Name], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[Id1], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Level4_Name], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[OneToOne_Required_PK_Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Level2_Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [t0].[Id] AS [Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Level3_Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t2].[Id] AS [Id1], [t2].[Level3_Optional_Id], [t2].[Level3_Required_Id], [t2].[Level4_Name], [t2].[OneToMany_Optional_Inverse4Id], [t2].[OneToMany_Required_Inverse4Id], [t2].[OneToOne_Optional_PK_Inverse4Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l3] + WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse3Id] IS NOT NULL + ) AS [t0] ON CASE + WHEN [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l2].[Id] + END = [t0].[OneToOne_Optional_PK_Inverse3Id] + LEFT JOIN ( + SELECT [l4].[Id], [l4].[Level3_Optional_Id], [l4].[Level3_Required_Id], [l4].[Level4_Name], [l4].[OneToMany_Optional_Inverse4Id], [l4].[OneToMany_Required_Inverse4Id], [l4].[OneToOne_Optional_PK_Inverse4Id] + FROM [Level1] AS [l4] + WHERE [l4].[Level3_Required_Id] IS NOT NULL AND [l4].[OneToMany_Required_Inverse4Id] IS NOT NULL + ) AS [t2] ON CASE + WHEN [t0].[Level2_Required_Id] IS NOT NULL AND [t0].[OneToMany_Required_Inverse3Id] IS NOT NULL THEN [t0].[Id] + END = [t2].[Level3_Optional_Id] + WHERE [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t1] ON [l].[Id] = [t1].[OneToMany_Optional_Inverse2Id] +WHERE ( + SELECT COUNT(*) + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Level3_Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l1] + WHERE [l1].[Level2_Required_Id] IS NOT NULL AND [l1].[OneToMany_Required_Inverse3Id] IS NOT NULL + ) AS [t] ON CASE + WHEN [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l0].[Id] + END = [t].[OneToOne_Optional_PK_Inverse3Id] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL AND [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] AND ([t].[Level3_Name] <> N'Foo' OR [t].[Level3_Name] IS NULL)) > 0 +ORDER BY [l].[Id], [t1].[Id], [t1].[Id0]"); + } + + public override async Task Filtered_include_basic_Where_EF_Property(bool async) + { + await base.Filtered_include_basic_Where_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL AND [l0].[Id] > 5 +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id]"); + } + + public override async Task Filtered_include_OrderBy_EF_Property(bool async) + { + await base.Filtered_include_OrderBy_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t].[Level2_Name]"); + } + + public override async Task Filtered_include_basic_OrderBy_Skip_Take_EF_Property(bool async) + { + await base.Filtered_include_basic_OrderBy_Skip_Take_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t0].[Id], [t0].[OneToOne_Required_PK_Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Level2_Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id] + FROM ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Level2_Name]) AS [row] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t0].[OneToMany_Optional_Inverse2Id], [t0].[Level2_Name]"); + } + + public override async Task Filtered_include_on_ThenInclude_EF_Property(bool async) + { + await base.Filtered_include_on_ThenInclude_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Level3_Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t1].[Id], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Level3_Name], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id] + FROM ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Level3_Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Optional_Inverse3Id] ORDER BY [l1].[Level3_Name]) AS [row] + FROM [Level1] AS [l1] + WHERE [l1].[Level2_Required_Id] IS NOT NULL AND [l1].[OneToMany_Required_Inverse3Id] IS NOT NULL AND ([l1].[Level3_Name] <> N'Foo' OR [l1].[Level3_Name] IS NULL) + ) AS [t1] + WHERE 1 < [t1].[row] AND [t1].[row] <= 4 +) AS [t0] ON CASE + WHEN [t].[OneToOne_Required_PK_Date] IS NOT NULL AND [t].[Level1_Required_Id] IS NOT NULL AND [t].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [t].[Id] +END = [t0].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [t].[Id], [t0].[OneToMany_Optional_Inverse3Id], [t0].[Level3_Name]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs index 122d22924fd..793a61e4db2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs @@ -3442,6 +3442,171 @@ GROUP BY [l2].[Name] ORDER BY [l].[Id], [l0].[Id], [l1].[Id]"); } + public override async Task Multiple_complex_includes_EF_Property(bool async) + { + await base.Multiple_complex_includes_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +ORDER BY [l].[Id], [l0].[Id]", + // + @"SELECT [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], [l].[Id], [l0].[Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +INNER JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [l0].[Id]", + // + @"SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [l].[Id], [l0].[Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +INNER JOIN ( + SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [l2].[Id] AS [Id0], [l2].[Level2_Optional_Id], [l2].[Level2_Required_Id], [l2].[Name] AS [Name0], [l2].[OneToMany_Optional_Inverse3Id], [l2].[OneToMany_Optional_Self_Inverse3Id], [l2].[OneToMany_Required_Inverse3Id], [l2].[OneToMany_Required_Self_Inverse3Id], [l2].[OneToOne_Optional_PK_Inverse3Id], [l2].[OneToOne_Optional_Self3Id] + FROM [LevelTwo] AS [l1] + LEFT JOIN [LevelThree] AS [l2] ON [l1].[Id] = [l2].[Level2_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Id]"); + } + + public override async Task Multiple_complex_includes_self_ref_EF_Property(bool async) + { + await base.Multiple_complex_includes_self_ref_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Name], [l0].[OneToMany_Optional_Self_Inverse1Id], [l0].[OneToMany_Required_Self_Inverse1Id], [l0].[OneToOne_Optional_Self1Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelOne] AS [l0] ON [l].[OneToOne_Optional_Self1Id] = [l0].[Id] +ORDER BY [l].[Id], [l0].[Id]", + // + @"SELECT [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_Inverse1Id], [l1].[OneToMany_Required_Self_Inverse1Id], [l1].[OneToOne_Optional_Self1Id], [l].[Id], [l0].[Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelOne] AS [l0] ON [l].[OneToOne_Optional_Self1Id] = [l0].[Id] +INNER JOIN [LevelOne] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Self_Inverse1Id] +ORDER BY [l].[Id], [l0].[Id]", + // + @"SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [t].[Id0], [t].[Date0], [t].[Name0], [t].[OneToMany_Optional_Self_Inverse1Id0], [t].[OneToMany_Required_Self_Inverse1Id0], [t].[OneToOne_Optional_Self1Id0], [l].[Id], [l0].[Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelOne] AS [l0] ON [l].[OneToOne_Optional_Self1Id] = [l0].[Id] +INNER JOIN ( + SELECT [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_Inverse1Id], [l1].[OneToMany_Required_Self_Inverse1Id], [l1].[OneToOne_Optional_Self1Id], [l2].[Id] AS [Id0], [l2].[Date] AS [Date0], [l2].[Name] AS [Name0], [l2].[OneToMany_Optional_Self_Inverse1Id] AS [OneToMany_Optional_Self_Inverse1Id0], [l2].[OneToMany_Required_Self_Inverse1Id] AS [OneToMany_Required_Self_Inverse1Id0], [l2].[OneToOne_Optional_Self1Id] AS [OneToOne_Optional_Self1Id0] + FROM [LevelOne] AS [l1] + LEFT JOIN [LevelOne] AS [l2] ON [l1].[OneToOne_Optional_Self1Id] = [l2].[Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Self_Inverse1Id] +ORDER BY [l].[Id], [l0].[Id]"); + } + + public override async Task Include_collection_multiple_with_filter_EF_Property(bool async) + { + await base.Include_collection_multiple_with_filter_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] +FROM [LevelOne] AS [l] +WHERE ( + SELECT COUNT(*) + FROM [LevelTwo] AS [l0] + LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToOne_Optional_PK_Inverse3Id] + WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] AND ([l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL)) > 0 +ORDER BY [l].[Id]", + // + @"SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t].[Id1], [t].[Level3_Optional_Id], [t].[Level3_Required_Id], [t].[Name1], [t].[OneToMany_Optional_Inverse4Id], [t].[OneToMany_Optional_Self_Inverse4Id], [t].[OneToMany_Required_Inverse4Id], [t].[OneToMany_Required_Self_Inverse4Id], [t].[OneToOne_Optional_PK_Inverse4Id], [t].[OneToOne_Optional_Self4Id], [l].[Id] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [l3].[Id] AS [Id0], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id], [l4].[Id] AS [Id1], [l4].[Level3_Optional_Id], [l4].[Level3_Required_Id], [l4].[Name] AS [Name1], [l4].[OneToMany_Optional_Inverse4Id], [l4].[OneToMany_Optional_Self_Inverse4Id], [l4].[OneToMany_Required_Inverse4Id], [l4].[OneToMany_Required_Self_Inverse4Id], [l4].[OneToOne_Optional_PK_Inverse4Id], [l4].[OneToOne_Optional_Self4Id] + FROM [LevelTwo] AS [l2] + LEFT JOIN [LevelThree] AS [l3] ON [l2].[Id] = [l3].[OneToOne_Optional_PK_Inverse3Id] + LEFT JOIN [LevelFour] AS [l4] ON [l3].[Id] = [l4].[Level3_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +WHERE ( + SELECT COUNT(*) + FROM [LevelTwo] AS [l0] + LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToOne_Optional_PK_Inverse3Id] + WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] AND ([l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL)) > 0 +ORDER BY [l].[Id]"); + } + + public override async Task Filtered_include_basic_Where_EF_Property(bool async) + { + await base.Filtered_include_basic_Where_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] +FROM [LevelOne] AS [l] +ORDER BY [l].[Id]", + // + @"SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [l].[Id] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] > 5 +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id]"); + } + + public override async Task Filtered_include_OrderBy_EF_Property(bool async) + { + await base.Filtered_include_OrderBy_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] +FROM [LevelOne] AS [l] +ORDER BY [l].[Id]", + // + @"SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l].[Id] +FROM [LevelOne] AS [l] +INNER JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Name]"); + } + + public override async Task Filtered_include_basic_OrderBy_Skip_Take_EF_Property(bool async) + { + await base.Filtered_include_basic_OrderBy_Skip_Take_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] +FROM [LevelOne] AS [l] +ORDER BY [l].[Id]", + // + @"SELECT [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id], [l].[Id] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id] + FROM ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Name]) AS [row] + FROM [LevelTwo] AS [l0] + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t0].[OneToMany_Optional_Inverse2Id], [t0].[Name]"); + } + + public override async Task Filtered_include_on_ThenInclude_EF_Property(bool async) + { + await base.Filtered_include_on_ThenInclude_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +ORDER BY [l].[Id], [l0].[Id]", + // + @"SELECT [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [l].[Id], [l0].[Id] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +INNER JOIN ( + SELECT [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id] + FROM ( + SELECT [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], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Optional_Inverse3Id] ORDER BY [l1].[Name]) AS [row] + FROM [LevelThree] AS [l1] + WHERE [l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l0].[Id] = [t0].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [l0].[Id], [t0].[OneToMany_Optional_Inverse3Id], [t0].[Name]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 52e830f3199..8b02e9c1134 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -3961,6 +3961,43 @@ public override async Task Project_shadow_properties2(bool async) FROM [LevelTwo] AS [l]"); } + public override async Task Multiple_required_navigation_using_multiple_selects_with_EF_Property_Include(bool async) + { + await base.Multiple_required_navigation_using_multiple_selects_with_EF_Property_Include(async); + + AssertSql(); + } + + public override async Task SelectMany_with_EF_Property_Include1(bool async) + { + await base.SelectMany_with_EF_Property_Include1(async); + + AssertSql( + @"SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [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 [LevelOne] AS [l] +INNER JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] +LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[Level2_Required_Id]"); + } + + public override async Task Multiple_SelectMany_with_EF_Property_Include(bool async) + { + await base.Multiple_SelectMany_with_EF_Property_Include(async); + + AssertSql( + @"SELECT [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], [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id] +FROM [LevelOne] AS [l] +INNER JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] +INNER JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id] +LEFT JOIN [LevelFour] AS [l2] ON [l1].[Id] = [l2].[Level3_Required_Id]"); + } + + public override async Task Multiple_required_navigation_with_EF_Property_Include(bool async) + { + await base.Multiple_required_navigation_with_EF_Property_Include(async); + + AssertSql(); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs index fdb96315aad..0a923d0fa14 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs @@ -7229,6 +7229,69 @@ public override async Task Project_shadow_properties5(bool async) FROM [InheritanceOne] AS [i]"); } + public override async Task Multiple_required_navigation_using_multiple_selects_with_EF_Property_Include(bool async) + { + await base.Multiple_required_navigation_using_multiple_selects_with_EF_Property_Include(async); + + AssertSql(); + } + + public override async Task SelectMany_with_EF_Property_Include1(bool async) + { + await base.SelectMany_with_EF_Property_Include1(async); + + AssertSql( + @"SELECT [t].[Id], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Level3_Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id] +FROM [Level1] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +LEFT JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Level3_Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l1] + WHERE [l1].[Level2_Required_Id] IS NOT NULL AND [l1].[OneToMany_Required_Inverse3Id] IS NOT NULL +) AS [t0] ON CASE + WHEN [t].[OneToOne_Required_PK_Date] IS NOT NULL AND [t].[Level1_Required_Id] IS NOT NULL AND [t].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [t].[Id] +END = [t0].[Level2_Required_Id]"); + } + + public override async Task Multiple_SelectMany_with_EF_Property_Include(bool async) + { + await base.Multiple_SelectMany_with_EF_Property_Include(async); + + AssertSql( + @"SELECT [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Level3_Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t1].[Id], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Level4_Name], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id] +FROM [Level1] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Required_Id], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +INNER JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Level3_Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id] + FROM [Level1] AS [l1] + WHERE [l1].[Level2_Required_Id] IS NOT NULL AND [l1].[OneToMany_Required_Inverse3Id] IS NOT NULL +) AS [t0] ON CASE + WHEN [t].[OneToOne_Required_PK_Date] IS NOT NULL AND [t].[Level1_Required_Id] IS NOT NULL AND [t].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [t].[Id] +END = [t0].[OneToMany_Optional_Inverse3Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Level4_Name], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id] + FROM [Level1] AS [l2] + WHERE [l2].[Level3_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse4Id] IS NOT NULL +) AS [t1] ON CASE + WHEN [t0].[Level2_Required_Id] IS NOT NULL AND [t0].[OneToMany_Required_Inverse3Id] IS NOT NULL THEN [t0].[Id] +END = [t1].[Level3_Required_Id]"); + } + + public override async Task Multiple_required_navigation_with_EF_Property_Include(bool async) + { + await base.Multiple_required_navigation_with_EF_Property_Include(async); + + AssertSql(); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 48f76cf08e8..7900a1f0a1b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -8650,6 +8650,39 @@ FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName]))"); } + public override async Task Include_reference_on_derived_type_using_EF_Property(bool async) + { + await base.Include_reference_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId], [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] +FROM [LocustLeaders] AS [l] +LEFT JOIN [Gears] AS [g] ON [l].[DefeatedByNickname] = [g].[Nickname] AND [l].[DefeatedBySquadId] = [g].[SquadId]"); + } + + public override async Task Include_collection_on_derived_type_using_EF_Property(bool async) + { + await base.Include_collection_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank] +FROM [Gears] AS [g] +LEFT JOIN [Gears] AS [g0] ON [g].[Nickname] = [g0].[LeaderNickname] AND [g].[SquadId] = [g0].[LeaderSquadId] +ORDER BY [g].[Nickname], [g].[SquadId], [g0].[Nickname]"); + } + + public override async Task EF_Property_based_Include_navigation_on_derived_type(bool async) + { + await base.EF_Property_based_Include_navigation_on_derived_type(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank] +FROM [Gears] AS [g] +LEFT JOIN [Gears] AS [g0] ON [g].[Nickname] = [g0].[LeaderNickname] AND [g].[SquadId] = [g0].[LeaderSquadId] +WHERE [g].[Discriminator] = N'Officer' +ORDER BY [g].[Nickname], [g].[SquadId], [g0].[Nickname]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs index 09c5e45b5e1..45fb06075bb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs @@ -962,9 +962,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(" "); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(" "); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs index 882369ee16d..e8c34c5878c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs @@ -961,9 +961,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(" "); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(" "); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs new file mode 100644 index 00000000000..fc3813106c5 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs @@ -0,0 +1,1994 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query; + +public class NorthwindEFPropertyIncludeQuerySqlServerTest : NorthwindEFPropertyIncludeQueryTestBase< + NorthwindQuerySqlServerFixture> +{ + // ReSharper disable once UnusedParameter.Local + public NorthwindEFPropertyIncludeQuerySqlServerTest(NorthwindQuerySqlServerFixture fixture) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + public override async Task Include_collection_with_last_no_orderby(bool async) + { + Assert.Equal( + RelationalStrings.LastUsedWithoutOrderBy(nameof(Enumerable.Last)), + (await Assert.ThrowsAsync( + () => base.Include_collection_with_last_no_orderby(async))).Message); + + AssertSql(); + } + + public override async Task Include_collection_with_filter_reordered(bool async) + { + await base.Include_collection_with_filter_reordered(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] = N'ALFKI' +ORDER BY [c].[CustomerID]"); + } + + public override async Task Include_collection_order_by_non_key_with_first_or_default(bool async) + { + await base.Include_collection_order_by_non_key_with_first_or_default(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(1) [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] + ORDER BY [c].[CompanyName] DESC +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CompanyName] DESC, [t].[CustomerID]"); + } + + public override async Task Include_with_cycle_does_not_throw_when_AsTracking_NoTrackingWithIdentityResolution(bool async) + { + await base.Include_with_cycle_does_not_throw_when_AsTracking_NoTrackingWithIdentityResolution(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [o].[OrderID] < 10800 +ORDER BY [o].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_collection_with_filter(bool async) + { + await base.Include_collection_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] = N'ALFKI' +ORDER BY [c].[CustomerID]"); + } + + public override async Task Include_references_then_include_multi_level(bool async) + { + await base.Include_references_then_include_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_collection_order_by_collection_column(bool async) + { + await base.Include_collection_order_by_collection_column(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderDate] DESC) AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'W%' + ORDER BY ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderDate] DESC) DESC +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [t].[CustomerID] = [o0].[CustomerID] +ORDER BY [t].[c] DESC, [t].[CustomerID]"); + } + + public override async Task Include_collection_alias_generation(bool async) + { + await base.Include_collection_alias_generation(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [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].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%') +ORDER BY [o].[OrderID], [o0].[OrderID]"); + } + + public override async Task Include_collection_skip_take_no_order_by(bool async) + { + await base.Include_collection_skip_take_no_order_by(async); + + AssertSql( + @"@__p_0='10' +@__p_1='5' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + 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] + ORDER BY (SELECT 1) + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_collection_with_cross_join_clause_with_filter(bool async) + { + await base.Include_collection_with_cross_join_clause_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +CROSS JOIN ( + SELECT TOP(5) [o].[OrderID] + FROM [Orders] AS [o] + ORDER BY [o].[OrderID] +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + + public override async Task Join_Include_reference_GroupBy_Select(bool async) + { + await base.Join_Include_reference_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [o0].[OrderID] + FROM [Order Details] AS [o] + INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] + GROUP BY [o0].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[CustomerID0], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region] + FROM ( + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + FROM [Order Details] AS [o1] + INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] + LEFT JOIN [Customers] AS [c] ON [o2].[CustomerID] = [c].[CustomerID] + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID]"); + } + + public override async Task Include_multi_level_reference_and_collection_predicate(bool async) + { + await base.Include_multi_level_reference_and_collection_predicate(async); + + AssertSql( + @"SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[CustomerID0], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(2) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Orders] AS [o] + LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] + WHERE [o].[OrderID] = 10248 +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [t].[CustomerID0] = [o0].[CustomerID] +ORDER BY [t].[OrderID], [t].[CustomerID0]"); + } + + public override async Task Include_references_then_include_collection(bool async) + { + await base.Include_references_then_include_collection(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%') +ORDER BY [o].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_collection_on_additional_from_clause_with_filter(bool async) + { + await base.Include_collection_on_additional_from_clause_with_filter(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [c].[CustomerID], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[CustomerID] = N'ALFKI' +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [c].[CustomerID], [t].[CustomerID]"); + } + + public override async Task Include_duplicate_reference3(bool async) + { + await base.Include_duplicate_reference3(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + ORDER BY [o].[OrderID] +) AS [t] +CROSS JOIN ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Orders] AS [o0] + ORDER BY [o0].[OrderID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY +) AS [t0] +LEFT JOIN [Customers] AS [c] ON [t0].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); + } + + public override async Task Include_collection_order_by_non_key_with_take(bool async) + { + await base.Include_collection_order_by_non_key_with_take(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[ContactTitle] +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[ContactTitle], [t].[CustomerID]"); + } + + public override async Task Include_collection_then_include_collection_predicate(bool async) + { + await base.Include_collection_then_include_collection_predicate(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] +FROM ( + SELECT TOP(2) [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' +) AS [t] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] +ORDER BY [t].[CustomerID], [t0].[OrderID], [t0].[OrderID0]"); + } + + public override async Task Include_collection_take_no_order_by(bool async) + { + await base.Include_collection_take_no_order_by(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [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] +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_collection_principal_already_tracked(bool async) + { + await base.Include_collection_principal_already_tracked(async); + + AssertSql( + @"SELECT TOP(2) [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'", + // + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(2) [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' +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_collection_OrderBy_object(bool async) + { + await base.Include_collection_OrderBy_object(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [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] < 10250 +ORDER BY [o].[OrderID], [o0].[OrderID]"); + } + + public override async Task Include_duplicate_collection_result_operator2(bool async) + { + await base.Include_duplicate_collection_result_operator2(async); + + AssertSql( + @"@__p_1='1' +@__p_0='2' + +SELECT [t1].[CustomerID], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region], [t1].[CustomerID0], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t1].[Address0], [t1].[City0], [t1].[CompanyName0], [t1].[ContactName0], [t1].[ContactTitle0], [t1].[Country0], [t1].[Fax0], [t1].[Phone0], [t1].[PostalCode0], [t1].[Region0] +FROM ( + SELECT TOP(@__p_1) [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t0].[CustomerID] AS [CustomerID0], [t0].[Address] AS [Address0], [t0].[City] AS [City0], [t0].[CompanyName] AS [CompanyName0], [t0].[ContactName] AS [ContactName0], [t0].[ContactTitle] AS [ContactTitle0], [t0].[Country] AS [Country0], [t0].[Fax] AS [Fax0], [t0].[Phone] AS [Phone0], [t0].[PostalCode] AS [PostalCode0], [t0].[Region] AS [Region0] + FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[CustomerID] + ) AS [t] + CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + ORDER BY [c0].[CustomerID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t0] + ORDER BY [t].[CustomerID] +) AS [t1] +LEFT JOIN [Orders] AS [o] ON [t1].[CustomerID] = [o].[CustomerID] +ORDER BY [t1].[CustomerID], [t1].[CustomerID0]"); + } + + public override async Task Repro9735(bool async) + { + await base.Repro9735(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[CustomerID0], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID] AS [CustomerID0], CASE + WHEN [c].[CustomerID] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [c], CASE + WHEN [c].[CustomerID] IS NOT NULL THEN [c].[CustomerID] + ELSE N'' + END AS [c0] + FROM [Orders] AS [o] + LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] + ORDER BY CASE + WHEN [c].[CustomerID] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END, CASE + WHEN [c].[CustomerID] IS NOT NULL THEN [c].[CustomerID] + ELSE N'' + END +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[c], [t].[c0], [t].[OrderID], [t].[CustomerID0], [o0].[OrderID]"); + } + + public override async Task Include_collection_single_or_default_no_result(bool async) + { + await base.Include_collection_single_or_default_no_result(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(2) [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 ?' +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_collection_with_cross_apply_with_filter(bool async) + { + await base.Include_collection_with_cross_apply_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +CROSS APPLY ( + SELECT TOP(5) [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[CustomerID] = [c].[CustomerID] + ORDER BY [c].[CustomerID] +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + + public override async Task Include_collection_with_left_join_clause_with_filter(bool async) + { + await base.Include_collection_with_left_join_clause_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Include_duplicate_collection(bool async) + { + await base.Include_duplicate_collection(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t0].[CustomerID], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[CustomerID] +) AS [t] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + ORDER BY [c0].[CustomerID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY +) AS [t0] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [t0].[CustomerID] = [o0].[CustomerID] +ORDER BY [t].[CustomerID], [t0].[CustomerID], [o].[OrderID]"); + } + + public override async Task Include_collection(bool async) + { + await base.Include_collection(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID]"); + } + + public override async Task Include_collection_then_include_collection_then_include_reference(bool async) + { + await base.Include_collection_then_include_collection_then_include_reference(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice], [t0].[ProductID0], [t0].[Discontinued], [t0].[ProductName], [t0].[SupplierID], [t0].[UnitPrice0], [t0].[UnitsInStock] +FROM [Customers] AS [c] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[OrderID] AS [OrderID0], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[ProductID0], [t].[Discontinued], [t].[ProductName], [t].[SupplierID], [t].[UnitPrice0], [t].[UnitsInStock] + FROM [Orders] AS [o] + LEFT JOIN ( + SELECT [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice], [p].[ProductID] AS [ProductID0], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice] AS [UnitPrice0], [p].[UnitsInStock] + FROM [Order Details] AS [o0] + INNER JOIN [Products] AS [p] ON [o0].[ProductID] = [p].[ProductID] + ) AS [t] ON [o].[OrderID] = [t].[OrderID] +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); + } + + public override async Task Include_reference_GroupBy_Select(bool async) + { + await base.Include_reference_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[CustomerID0], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region] + FROM ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(PARTITION BY [o0].[OrderID] ORDER BY [o0].[OrderID]) AS [row] + FROM [Orders] AS [o0] + LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] + WHERE [o0].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID]"); + } + + public override async Task Include_multiple_references_multi_level_reverse(bool async) + { + await base.Include_multiple_references_multi_level_reverse(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Order Details] AS [o] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_collection_with_join_clause_with_filter(bool async) + { + await base.Include_collection_with_join_clause_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Include_collection_OrderBy_list_does_not_contains(bool async) + { + await base.Include_collection_OrderBy_list_does_not_contains(async); + + AssertSql( + @"@__p_1='1' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE + WHEN [c].[CustomerID] <> N'ALFKI' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY CASE + WHEN [c].[CustomerID] <> N'ALFKI' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END + OFFSET @__p_1 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[c], [t].[CustomerID]"); + } + + public override async Task Include_reference_dependent_already_tracked(bool async) + { + await base.Include_reference_dependent_already_tracked(async); + + AssertSql( + @"SELECT TOP(2) [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'", + // + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[CustomerID] = N'ALFKI'"); + } + + public override async Task Include_reference_with_filter(bool async) + { + await base.Include_reference_with_filter(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[CustomerID] = N'ALFKI'"); + } + + public override async Task Include_duplicate_reference(bool async) + { + await base.Include_duplicate_reference(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + ORDER BY [o].[CustomerID], [o].[OrderID] +) AS [t] +CROSS JOIN ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Orders] AS [o0] + ORDER BY [o0].[CustomerID], [o0].[OrderID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY +) AS [t0] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Customers] AS [c0] ON [t0].[CustomerID] = [c0].[CustomerID] +ORDER BY [t].[CustomerID], [t].[OrderID]"); + } + + public override async Task Include_with_complex_projection(bool async) + { + await base.Include_with_complex_projection(async); + + AssertSql( + @"SELECT [c].[CustomerID] AS [Id] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID]"); + } + + public override async Task Include_collection_order_by_non_key_with_skip(bool async) + { + await base.Include_collection_order_by_non_key_with_skip(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + 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] LIKE N'F%' + ORDER BY [c].[ContactTitle] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[ContactTitle], [t].[CustomerID]"); + } + + public override async Task Include_collection_on_join_clause_with_order_by_and_filter(bool async) + { + await base.Include_collection_on_join_clause_with_order_by_and_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] = N'ALFKI' +ORDER BY [c].[City], [c].[CustomerID], [o].[OrderID]"); + } + + public override async Task Multi_level_includes_are_applied_with_take(bool async) + { + await base.Multi_level_includes_are_applied_with_take(async); + + AssertSql( + @"@__p_0='1' + +SELECT [t0].[CustomerID], [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID], [t1].[Discount], [t1].[Quantity], [t1].[UnitPrice] +FROM ( + SELECT TOP(1) [t].[CustomerID] + FROM ( + SELECT TOP(@__p_0) [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + ) AS [t] + ORDER BY [t].[CustomerID] +) AS [t0] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] +ORDER BY [t0].[CustomerID], [t1].[OrderID], [t1].[OrderID0]"); + } + + public override async Task Include_multiple_references_then_include_collection_multi_level_reverse(bool async) + { + await base.Include_multiple_references_then_include_collection_multi_level_reverse(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13 +ORDER BY [o].[OrderID], [o].[ProductID], [p].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_collection_then_reference(bool async) + { + await base.Include_collection_then_reference(async); + + AssertSql( + @"SELECT [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [t].[OrderID], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[OrderID0], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] +FROM [Products] AS [p] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID] AS [OrderID0], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Order Details] AS [o] + INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t] ON [p].[ProductID] = [t].[ProductID] +WHERE ([p].[ProductID] % 17) = 5 +ORDER BY [p].[ProductID], [t].[OrderID], [t].[ProductID]"); + } + + public override async Task Include_collection_order_by_key(bool async) + { + await base.Include_collection_order_by_key(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID]"); + } + + public override async Task Include_collection_with_outer_apply_with_filter(bool async) + { + await base.Include_collection_with_outer_apply_with_filter(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +OUTER APPLY ( + SELECT TOP(5) [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[CustomerID] = [c].[CustomerID] + ORDER BY [c].[CustomerID] +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + + public override async Task Include_collection_on_additional_from_clause2(bool async) + { + await base.Include_collection_on_additional_from_clause2(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] +FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[CustomerID] +) AS [t] +CROSS JOIN [Customers] AS [c0] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_collection_dependent_already_tracked(bool async) + { + await base.Include_collection_dependent_already_tracked(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +WHERE [o].[CustomerID] = N'ALFKI'", + // + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(2) [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' +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_with_complex_projection_does_not_change_ordering_of_projection(bool async) + { + await base.Include_with_complex_projection_does_not_change_ordering_of_projection(async); + + AssertSql( + @"SELECT [c].[CustomerID] AS [Id], ( + SELECT COUNT(*) + FROM [Orders] AS [o0] + WHERE [c].[CustomerID] = [o0].[CustomerID]) AS [TotalOrders] +FROM [Customers] AS [c] +WHERE [c].[ContactTitle] = N'Owner' AND ( + SELECT COUNT(*) + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID]) > 2 +ORDER BY [c].[CustomerID]"); + } + + public override async Task Include_multi_level_collection_and_then_include_reference_predicate(bool async) + { + await base.Include_multi_level_collection_and_then_include_reference_predicate(async); + + AssertSql( + @"SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t0].[OrderID], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice], [t0].[ProductID0], [t0].[Discontinued], [t0].[ProductName], [t0].[SupplierID], [t0].[UnitPrice0], [t0].[UnitsInStock] +FROM ( + SELECT TOP(2) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] = 10248 +) AS [t] +LEFT JOIN ( + SELECT [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice], [p].[ProductID] AS [ProductID0], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice] AS [UnitPrice0], [p].[UnitsInStock] + FROM [Order Details] AS [o0] + INNER JOIN [Products] AS [p] ON [o0].[ProductID] = [p].[ProductID] +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[ProductID]"); + } + + public override async Task Multi_level_includes_are_applied_with_skip_take(bool async) + { + await base.Multi_level_includes_are_applied_with_skip_take(async); + + AssertSql( + @"@__p_0='1' + +SELECT [t0].[CustomerID], [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID], [t1].[Discount], [t1].[Quantity], [t1].[UnitPrice] +FROM ( + SELECT TOP(1) [t].[CustomerID] + FROM ( + SELECT [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY + ) AS [t] + ORDER BY [t].[CustomerID] +) AS [t0] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] +ORDER BY [t0].[CustomerID], [t1].[OrderID], [t1].[OrderID0]"); + } + + public override async Task Include_collection_OrderBy_empty_list_contains(bool async) + { + await base.Include_collection_OrderBy_empty_list_contains(async); + + AssertSql( + @"@__p_1='1' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(0 AS bit) AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY (SELECT 1) + OFFSET @__p_1 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[c], [t].[CustomerID]"); + } + + public override async Task Include_references_and_collection_multi_level(bool async) + { + await base.Include_references_and_collection_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13 AND [o].[UnitPrice] < 10.0 +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_collection_force_alias_uniquefication(bool async) + { + await base.Include_collection_force_alias_uniquefication(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [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].[CustomerID] = N'ALFKI' +ORDER BY [o].[OrderID], [o0].[OrderID]"); + } + + public override async Task Include_collection_with_outer_apply_with_filter_non_equality(bool async) + { + await base.Include_collection_with_outer_apply_with_filter_non_equality(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Customers] AS [c] +OUTER APPLY ( + SELECT TOP(5) [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[CustomerID] <> [c].[CustomerID] OR [o].[CustomerID] IS NULL + ORDER BY [c].[CustomerID] +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + + public override async Task Include_in_let_followed_by_FirstOrDefault(bool async) + { + await base.Include_in_let_followed_by_FirstOrDefault(async); + + AssertSql( + @"SELECT [c].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Customers] AS [c] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderDate]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +LEFT JOIN [Order Details] AS [o0] ON [t0].[OrderID] = [o0].[OrderID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t0].[OrderID], [o0].[OrderID]"); + } + + public override async Task Include_references_multi_level(bool async) + { + await base.Include_references_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_collection_then_include_collection(bool async) + { + await base.Include_collection_then_include_collection(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[OrderID0], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice] +FROM [Customers] AS [c] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t] ON [c].[CustomerID] = [t].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderID], [t].[OrderID0]"); + } + + public override async Task Include_collection_with_multiple_conditional_order_by(bool async) + { + await base.Include_collection_with_multiple_conditional_order_by(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[CustomerID0], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID] AS [CustomerID0], CASE + WHEN [o].[OrderID] > 0 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [c], CASE + WHEN [c].[CustomerID] IS NOT NULL THEN [c].[City] + ELSE N'' + END AS [c0] + FROM [Orders] AS [o] + LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] + ORDER BY CASE + WHEN [o].[OrderID] > 0 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END, CASE + WHEN [c].[CustomerID] IS NOT NULL THEN [c].[City] + ELSE N'' + END +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[c], [t].[c0], [t].[OrderID], [t].[CustomerID0], [o0].[OrderID]"); + } + + public override async Task Include_reference_when_entity_in_projection(bool async) + { + await base.Include_reference_when_entity_in_projection(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%')"); + } + + public override async Task Include_reference_single_or_default_when_no_result(bool async) + { + await base.Include_reference_single_or_default_when_no_result(async); + + AssertSql( + @"SELECT TOP(2) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[OrderID] = -1"); + } + + public override async Task Include_reference_alias_generation(bool async) + { + await base.Include_reference_alias_generation(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_with_cycle_does_not_throw_when_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Include_with_cycle_does_not_throw_when_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [o].[OrderID] < 10800 +ORDER BY [o].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_references_then_include_collection_multi_level(bool async) + { + await base.Include_references_then_include_collection_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[ProductID] % 23) = 17 AND [o].[Quantity] < CAST(10 AS smallint) +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_reference_Join_GroupBy_Select(bool async) + { + await base.Include_reference_Join_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + INNER JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[CustomerID0], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region] + FROM ( + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + FROM [Orders] AS [o1] + INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] + LEFT JOIN [Customers] AS [c] ON [o1].[CustomerID] = [c].[CustomerID] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID]"); + } + + public override async Task Include_collection_when_projection(bool async) + { + await base.Include_collection_when_projection(async); + + AssertSql( + @"SELECT [c].[CustomerID] +FROM [Customers] AS [c]"); + } + + public override async Task Include_reference_SelectMany_GroupBy_Select(bool async) + { + await base.Include_reference_SelectMany_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + CROSS JOIN [Order Details] AS [o0] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[CustomerID0], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region] + FROM ( + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + FROM [Orders] AS [o1] + CROSS JOIN [Order Details] AS [o2] + LEFT JOIN [Customers] AS [c] ON [o1].[CustomerID] = [c].[CustomerID] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID]"); + } + + public override async Task Include_multiple_references_then_include_collection_multi_level(bool async) + { + await base.Include_multiple_references_then_include_collection_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [p].[ProductID], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13 +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID], [p].[ProductID]"); + } + + public override async Task Outer_idenfier_correctly_determined_when_doing_include_on_right_side_of_left_join(bool async) + { + await base.Outer_idenfier_correctly_determined_when_doing_include_on_right_side_of_left_join(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE [c].[City] = N'Seattle' +ORDER BY [c].[CustomerID], [o].[OrderID], [o0].[OrderID]"); + } + + public override async Task SelectMany_Include_reference_GroupBy_Select(bool async) + { + await base.SelectMany_Include_reference_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [o0].[OrderID] + FROM [Order Details] AS [o] + CROSS JOIN [Orders] AS [o0] + WHERE [o].[OrderID] = 10248 + GROUP BY [o0].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[CustomerID0], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region] + FROM ( + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + FROM [Order Details] AS [o1] + CROSS JOIN [Orders] AS [o2] + LEFT JOIN [Customers] AS [c] ON [o2].[CustomerID] = [c].[CustomerID] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID]"); + } + + public override async Task Include_collection_SelectMany_GroupBy_Select(bool async) + { + await base.Include_collection_SelectMany_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + CROSS JOIN [Order Details] AS [o0] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] + FROM ( + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + FROM [Orders] AS [o1] + CROSS JOIN [Order Details] AS [o2] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); + } + + public override async Task Include_collection_OrderBy_list_contains(bool async) + { + await base.Include_collection_OrderBy_list_contains(async); + + AssertSql( + @"@__p_1='1' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE + WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY CASE + WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END + OFFSET @__p_1 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[c], [t].[CustomerID]"); + } + + public override async Task Multi_level_includes_are_applied_with_skip(bool async) + { + await base.Multi_level_includes_are_applied_with_skip(async); + + AssertSql( + @"@__p_0='1' + +SELECT [t].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] +FROM ( + SELECT [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY +) AS [t] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] +ORDER BY [t].[CustomerID], [t0].[OrderID], [t0].[OrderID0]"); + } + + public override async Task Include_collection_on_additional_from_clause(bool async) + { + await base.Include_collection_on_additional_from_clause(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region], [t].[CustomerID], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [c].[CustomerID] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] +) AS [t] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[CustomerID] LIKE N'F%' +) AS [t0] +LEFT JOIN [Orders] AS [o] ON [t0].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID], [t0].[CustomerID]"); + } + + public override async Task Include_reference_distinct_is_server_evaluated(bool async) + { + await base.Include_reference_distinct_is_server_evaluated(async); + + AssertSql( + @"SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM ( + SELECT DISTINCT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10250 +) AS [t] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID]"); + } + + public override async Task Include_collection_distinct_is_server_evaluated(bool async) + { + await base.Include_collection_distinct_is_server_evaluated(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT DISTINCT [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] LIKE N'A%' +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_reference_when_projection(bool async) + { + await base.Include_reference_when_projection(async); + + AssertSql( + @"SELECT [o].[CustomerID] +FROM [Orders] AS [o]"); + } + + public override async Task Include_duplicate_collection_result_operator(bool async) + { + await base.Include_duplicate_collection_result_operator(async); + + AssertSql( + @"@__p_1='1' +@__p_0='2' + +SELECT [t1].[CustomerID], [t1].[Address], [t1].[City], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Country], [t1].[Fax], [t1].[Phone], [t1].[PostalCode], [t1].[Region], [t1].[CustomerID0], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t1].[Address0], [t1].[City0], [t1].[CompanyName0], [t1].[ContactName0], [t1].[ContactTitle0], [t1].[Country0], [t1].[Fax0], [t1].[Phone0], [t1].[PostalCode0], [t1].[Region0], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(@__p_1) [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t0].[CustomerID] AS [CustomerID0], [t0].[Address] AS [Address0], [t0].[City] AS [City0], [t0].[CompanyName] AS [CompanyName0], [t0].[ContactName] AS [ContactName0], [t0].[ContactTitle] AS [ContactTitle0], [t0].[Country] AS [Country0], [t0].[Fax] AS [Fax0], [t0].[Phone] AS [Phone0], [t0].[PostalCode] AS [PostalCode0], [t0].[Region] AS [Region0] + FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[CustomerID] + ) AS [t] + CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + ORDER BY [c0].[CustomerID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t0] + ORDER BY [t].[CustomerID] +) AS [t1] +LEFT JOIN [Orders] AS [o] ON [t1].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [t1].[CustomerID0] = [o0].[CustomerID] +ORDER BY [t1].[CustomerID], [t1].[CustomerID0], [o].[OrderID]"); + } + + public override async Task Include_reference(bool async) + { + await base.Include_reference(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%')"); + } + + public override async Task Include_multiple_references_and_collection_multi_level_reverse(bool async) + { + await base.Include_multiple_references_and_collection_multi_level_reverse(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13 +ORDER BY [o].[OrderID], [o].[ProductID], [p].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_closes_reader(bool async) + { + await base.Include_closes_reader(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(1) [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] +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]", + // + @"SELECT [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Products] AS [p]"); + } + + public override async Task Include_with_skip(bool async) + { + await base.Include_with_skip(async); + + AssertSql( + @"@__p_0='80' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + 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] + ORDER BY [c].[ContactName] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[ContactName], [t].[CustomerID]"); + } + + public override async Task Include_collection_Join_GroupBy_Select(bool async) + { + await base.Include_collection_Join_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + INNER JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] + FROM ( + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + FROM [Orders] AS [o1] + INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); + } + + public override async Task Include_collection_GroupBy_Select(bool async) + { + await base.Include_collection_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o1].[OrderID], [o1].[ProductID], [o1].[Discount], [o1].[Quantity], [o1].[UnitPrice] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[OrderID] = 10248 + GROUP BY [o].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + FROM ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o0].[OrderID] ORDER BY [o0].[OrderID]) AS [row] + FROM [Orders] AS [o0] + WHERE [o0].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +LEFT JOIN [Order Details] AS [o1] ON [t0].[OrderID] = [o1].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID], [o1].[OrderID]"); + } + + public override async Task Include_collection_orderby_take(bool async) + { + await base.Include_collection_orderby_take(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[CustomerID] +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Join_Include_collection_GroupBy_Select(bool async) + { + await base.Join_Include_collection_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] +FROM ( + SELECT [o0].[OrderID] + FROM [Order Details] AS [o] + INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] + WHERE [o].[OrderID] = 10248 + GROUP BY [o0].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] + FROM ( + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + FROM [Order Details] AS [o1] + INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); + } + + public override async Task Include_collection_order_by_non_key(bool async) + { + await base.Include_collection_order_by_non_key(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[PostalCode], [c].[CustomerID]"); + } + + public override async Task Include_when_result_operator(bool async) + { + await base.Include_when_result_operator(async); + + AssertSql( + @"SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM [Customers] AS [c]) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task Include_duplicate_reference2(bool async) + { + await base.Include_duplicate_reference2(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + ORDER BY [o].[OrderID] +) AS [t] +CROSS JOIN ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Orders] AS [o0] + ORDER BY [o0].[OrderID] + OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY +) AS [t0] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); + } + + public override async Task Include_collection_and_reference(bool async) + { + await base.Include_collection_and_reference(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%') +ORDER BY [o].[OrderID], [c].[CustomerID], [o0].[OrderID]"); + } + + public override async Task Include_multiple_references_multi_level(bool async) + { + await base.Include_multiple_references_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_references_and_collection_multi_level_predicate(bool async) + { + await base.Include_references_and_collection_multi_level_predicate(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE [o].[OrderID] = 10248 +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task SelectMany_Include_collection_GroupBy_Select(bool async) + { + await base.SelectMany_Include_collection_GroupBy_Select(async); + + AssertSql( + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] +FROM ( + SELECT [o0].[OrderID] + FROM [Order Details] AS [o] + CROSS JOIN [Orders] AS [o0] + WHERE [o].[OrderID] = 10248 + GROUP BY [o0].[OrderID] +) AS [t] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] + FROM ( + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + FROM [Order Details] AS [o1] + CROSS JOIN [Orders] AS [o2] + WHERE [o1].[OrderID] = 10248 + ) AS [t1] + WHERE [t1].[row] <= 1 +) AS [t0] ON [t].[OrderID] = [t0].[OrderID] +LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); + } + + public override async Task Include_collection_with_last(bool async) + { + await base.Include_collection_with_last(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(1) [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] + ORDER BY [c].[CompanyName] DESC +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CompanyName] DESC, [t].[CustomerID]"); + } + + public override async Task Include_collection_OrderBy_empty_list_does_not_contains(bool async) + { + await base.Include_collection_OrderBy_empty_list_does_not_contains(async); + + AssertSql( + @"@__p_1='1' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(1 AS bit) AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY (SELECT 1) + OFFSET @__p_1 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[c], [t].[CustomerID]"); + } + + public override async Task Include_multiple_references_then_include_multi_level_reverse(bool async) + { + await base.Include_multiple_references_then_include_multi_level_reverse(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Order Details] AS [o] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_reference_and_collection(bool async) + { + await base.Include_reference_and_collection(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%') +ORDER BY [o].[OrderID], [c].[CustomerID], [o0].[OrderID]"); + } + + public override async Task Include_is_not_ignored_when_projection_contains_client_method_and_complex_expression(bool async) + { + await base.Include_is_not_ignored_when_projection_contains_client_method_and_complex_expression(async); + + AssertSql( + @"SELECT CASE + WHEN [e0].[EmployeeID] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END, [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title], [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title] +FROM [Employees] AS [e] +LEFT JOIN [Employees] AS [e0] ON [e].[ReportsTo] = [e0].[EmployeeID] +WHERE [e].[EmployeeID] IN (1, 2) +ORDER BY [e].[EmployeeID]"); + } + + public override async Task Include_reference_with_filter_reordered(bool async) + { + await base.Include_reference_with_filter_reordered(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o].[CustomerID] = N'ALFKI'"); + } + + public override async Task Include_collection_order_by_subquery(bool async) + { + await base.Include_collection_order_by_subquery(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[EmployeeID]) AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] = N'ALFKI' + ORDER BY ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[EmployeeID]) +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [t].[CustomerID] = [o0].[CustomerID] +ORDER BY [t].[c], [t].[CustomerID]"); + } + + public override async Task Include_reference_and_collection_order_by(bool async) + { + await base.Include_reference_and_collection_order_by(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'F%') +ORDER BY [o].[OrderID], [c].[CustomerID]"); + } + + public override async Task Then_include_collection_order_by_collection_column(bool async) + { + await base.Then_include_collection_order_by_collection_column(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] +FROM ( + SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderDate] DESC) AS [c] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'W%' + ORDER BY ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderDate] DESC) DESC +) AS [t] +LEFT JOIN ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], [o1].[Discount], [o1].[Quantity], [o1].[UnitPrice] + FROM [Orders] AS [o0] + LEFT JOIN [Order Details] AS [o1] ON [o0].[OrderID] = [o1].[OrderID] +) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] +ORDER BY [t].[c] DESC, [t].[CustomerID], [t0].[OrderID], [t0].[OrderID0]"); + } + + public override async Task Include_multiple_references_then_include_multi_level(bool async) + { + await base.Include_multiple_references_then_include_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_collection_skip_no_order_by(bool async) + { + await base.Include_collection_skip_no_order_by(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + 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] + ORDER BY (SELECT 1) + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[CustomerID]"); + } + + public override async Task Include_multi_level_reference_then_include_collection_predicate(bool async) + { + await base.Include_multi_level_reference_then_include_collection_predicate(async); + + AssertSql( + @"SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[CustomerID0], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM ( + SELECT TOP(2) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID] AS [CustomerID0], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Orders] AS [o] + LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] + WHERE [o].[OrderID] = 10248 +) AS [t] +LEFT JOIN [Orders] AS [o0] ON [t].[CustomerID0] = [o0].[CustomerID] +ORDER BY [t].[OrderID], [t].[CustomerID0]"); + } + + public override async Task Include_multiple_references_and_collection_multi_level(bool async) + { + await base.Include_multiple_references_and_collection_multi_level(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [p].[ProductID], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE ([o].[OrderID] % 23) = 13 +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID], [p].[ProductID]"); + } + + public override async Task Include_where_skip_take_projection(bool async) + { + await base.Include_where_skip_take_projection(async); + + AssertSql( + @"@__p_0='1' +@__p_1='2' + +SELECT [o0].[CustomerID] +FROM ( + SELECT [o].[OrderID], [o].[ProductID] + FROM [Order Details] AS [o] + WHERE [o].[Quantity] = CAST(10 AS smallint) + ORDER BY [o].[OrderID], [o].[ProductID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +INNER JOIN [Orders] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [t].[ProductID]"); + } + + public override async Task Include_with_take(bool async) + { + await base.Include_with_take(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [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] + ORDER BY [c].[ContactName] DESC +) AS [t] +LEFT JOIN [Orders] AS [o] ON [t].[CustomerID] = [o].[CustomerID] +ORDER BY [t].[ContactName] DESC, [t].[CustomerID]"); + } + + public override async Task Include_multiple_references(bool async) + { + await base.Include_multiple_references(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] +WHERE ([o].[OrderID] % 23) = 13"); + } + + public override async Task Include_list(bool async) + { + await base.Include_list(async); + + AssertSql( + @"SELECT [p].[ProductID], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice], [p].[UnitsInStock], [t].[OrderID], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[OrderID0], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] +FROM [Products] AS [p] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID] AS [OrderID0], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Order Details] AS [o] + INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t] ON [p].[ProductID] = [t].[ProductID] +WHERE ([p].[ProductID] % 17) = 5 AND [p].[UnitPrice] < 20.0 +ORDER BY [p].[ProductID], [t].[OrderID], [t].[ProductID]"); + } + + public override async Task Include_empty_reference_sets_IsLoaded(bool async) + { + await base.Include_empty_reference_sets_IsLoaded(async); + + AssertSql( + @"SELECT TOP(1) [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title], [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title] +FROM [Employees] AS [e] +LEFT JOIN [Employees] AS [e0] ON [e].[ReportsTo] = [e0].[EmployeeID] +WHERE [e0].[EmployeeID] IS NULL"); + } + + public override async Task Include_references_then_include_collection_multi_level_predicate(bool async) + { + await base.Include_references_then_include_collection_multi_level_predicate(async); + + AssertSql( + @"SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o1] ON [c].[CustomerID] = [o1].[CustomerID] +WHERE [o].[OrderID] = 10248 +ORDER BY [o].[OrderID], [o].[ProductID], [o0].[OrderID], [c].[CustomerID]"); + } + + public override async Task Include_collection_with_conditional_order_by(bool async) + { + await base.Include_collection_with_conditional_order_by(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY CASE + WHEN [c].[CustomerID] LIKE N'S%' THEN 1 + ELSE 2 +END, [c].[CustomerID]"); + } + + public override async Task Include_non_existing_navigation(bool async) + { + await base.Include_non_existing_navigation(async); + + AssertSql(); + } + + public override async Task Include_property(bool async) + { + await base.Include_property(async); + + AssertSql(); + } + + public override async Task Include_property_after_navigation(bool async) + { + await base.Include_property_after_navigation(async); + + AssertSql(); + } + + public override async Task Include_property_expression_invalid(bool async) + { + await base.Include_property_expression_invalid(async); + + AssertSql(); + } + + public override async Task Then_include_property_expression_invalid(bool async) + { + await base.Then_include_property_expression_invalid(async); + + AssertSql(); + } + + public override async Task Filtered_include_with_multiple_ordering(bool async) + { + await base.Filtered_include_with_multiple_ordering(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] +FROM [Customers] AS [c] +OUTER APPLY ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderID] + OFFSET 1 ROWS +) AS [t] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t].[OrderDate] DESC"); + } + + public override async Task Include_specified_on_non_entity_not_supported(bool async) + { + await base.Include_specified_on_non_entity_not_supported(async); + + AssertSql(); + } + + public override async Task Include_collection_with_client_filter(bool async) + { + await base.Include_collection_with_client_filter(async); + + AssertSql(); + } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); + +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index 8a57dc4cc81..1b9f38a1369 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -6323,6 +6323,13 @@ public override async Task Select_subquery_recursive_trivial_returning_queryable AssertSql(); } + public override async Task EF_Property_include_on_incorrect_property_throws(bool async) + { + await base.EF_Property_include_on_incorrect_property_throws(async); + + AssertSql(); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index 1631afe231b..e3f09af5148 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -11722,6 +11722,71 @@ FROM [Weapons] AS [w] WHERE [t].[FullName] = [w].[OwnerFullName]))"); } + public override async Task Include_reference_on_derived_type_using_EF_Property(bool async) + { + await base.Include_reference_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [t].[Name], [t].[LocustHordeId], [t].[ThreatLevel], [t].[ThreatLevelByte], [t].[ThreatLevelNullableByte], [t].[DefeatedByNickname], [t].[DefeatedBySquadId], [t].[HighCommandId], [t].[Discriminator], [t0].[Nickname], [t0].[SquadId], [t0].[AssignedCityName], [t0].[CityOfBirthName], [t0].[FullName], [t0].[HasSoulPatch], [t0].[LeaderNickname], [t0].[LeaderSquadId], [t0].[Rank], [t0].[Discriminator] +FROM ( + SELECT [l].[Name], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], NULL AS [DefeatedByNickname], NULL AS [DefeatedBySquadId], NULL AS [HighCommandId], N'LocustLeader' AS [Discriminator] + FROM [LocustLeaders] AS [l] + UNION ALL + SELECT [l0].[Name], [l0].[LocustHordeId], [l0].[ThreatLevel], [l0].[ThreatLevelByte], [l0].[ThreatLevelNullableByte], [l0].[DefeatedByNickname], [l0].[DefeatedBySquadId], [l0].[HighCommandId], N'LocustCommander' AS [Discriminator] + FROM [LocustCommanders] AS [l0] +) AS [t] +LEFT JOIN ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator] + FROM [Gears] AS [g] + UNION ALL + SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] + FROM [Officers] AS [o] +) AS [t0] ON [t].[DefeatedByNickname] = [t0].[Nickname] AND [t].[DefeatedBySquadId] = [t0].[SquadId]"); + } + + public override async Task Include_collection_on_derived_type_using_EF_Property(bool async) + { + await base.Include_collection_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator], [t0].[Nickname], [t0].[SquadId], [t0].[AssignedCityName], [t0].[CityOfBirthName], [t0].[FullName], [t0].[HasSoulPatch], [t0].[LeaderNickname], [t0].[LeaderSquadId], [t0].[Rank], [t0].[Discriminator] +FROM ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator] + FROM [Gears] AS [g] + UNION ALL + SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] + FROM [Officers] AS [o] +) AS [t] +LEFT JOIN ( + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], N'Gear' AS [Discriminator] + FROM [Gears] AS [g0] + UNION ALL + SELECT [o0].[Nickname], [o0].[SquadId], [o0].[AssignedCityName], [o0].[CityOfBirthName], [o0].[FullName], [o0].[HasSoulPatch], [o0].[LeaderNickname], [o0].[LeaderSquadId], [o0].[Rank], N'Officer' AS [Discriminator] + FROM [Officers] AS [o0] +) AS [t0] ON [t].[Nickname] = [t0].[LeaderNickname] AND [t].[SquadId] = [t0].[LeaderSquadId] +ORDER BY [t].[Nickname], [t].[SquadId], [t0].[Nickname]"); + } + + public override async Task EF_Property_based_Include_navigation_on_derived_type(bool async) + { + await base.EF_Property_based_Include_navigation_on_derived_type(async); + + AssertSql( + @"SELECT [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator], [t0].[Nickname], [t0].[SquadId], [t0].[AssignedCityName], [t0].[CityOfBirthName], [t0].[FullName], [t0].[HasSoulPatch], [t0].[LeaderNickname], [t0].[LeaderSquadId], [t0].[Rank], [t0].[Discriminator] +FROM ( + SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] + FROM [Officers] AS [o] +) AS [t] +LEFT JOIN ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator] + FROM [Gears] AS [g] + UNION ALL + SELECT [o0].[Nickname], [o0].[SquadId], [o0].[AssignedCityName], [o0].[CityOfBirthName], [o0].[FullName], [o0].[HasSoulPatch], [o0].[LeaderNickname], [o0].[LeaderSquadId], [o0].[Rank], N'Officer' AS [Discriminator] + FROM [Officers] AS [o0] +) AS [t0] ON [t].[Nickname] = [t0].[LeaderNickname] AND [t].[SquadId] = [t0].[LeaderSquadId] +ORDER BY [t].[Nickname], [t].[SquadId], [t0].[Nickname]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs index 5d49430a139..9553fd55f73 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs @@ -1038,9 +1038,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(); } @@ -1802,7 +1802,52 @@ FROM [Leaves] AS [l] WHERE 0 = 1"); } + public override async Task Filtered_include_skip_navigation_order_by_take_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_take_EF_Property(async); + + AssertSql( + @"SELECT [e].[Key1], [e].[Key2], [e].[Key3], [e].[Name], [t0].[Id], [t0].[CollectionInverseId], [t0].[ExtraId], [t0].[Name], [t0].[ReferenceInverseId], [t0].[TwoSkipSharedId], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3] +FROM [EntityCompositeKeys] AS [e] +LEFT JOIN ( + SELECT [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t].[TwoSkipSharedId], [t].[CompositeKeySkipSharedKey1], [t].[CompositeKeySkipSharedKey2], [t].[CompositeKeySkipSharedKey3] + FROM ( + SELECT [e1].[Id], [e1].[CollectionInverseId], [e1].[ExtraId], [e1].[Name], [e1].[ReferenceInverseId], [e0].[TwoSkipSharedId], [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3], ROW_NUMBER() OVER(PARTITION BY [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3] ORDER BY [e1].[Id]) AS [row] + FROM [EntityCompositeKeyEntityTwo] AS [e0] + INNER JOIN [EntityTwos] AS [e1] ON [e0].[TwoSkipSharedId] = [e1].[Id] + ) AS [t] + WHERE [t].[row] <= 2 +) AS [t0] ON [e].[Key1] = [t0].[CompositeKeySkipSharedKey1] AND [e].[Key2] = [t0].[CompositeKeySkipSharedKey2] AND [e].[Key3] = [t0].[CompositeKeySkipSharedKey3] +ORDER BY [e].[Key1], [e].[Key2], [e].[Key3], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id]"); + } + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async); + + AssertSql( + @"SELECT [e].[Id], [e].[Name], [t1].[Id], [t1].[CollectionInverseId], [t1].[ExtraId], [t1].[Name], [t1].[ReferenceInverseId], [t1].[OneId], [t1].[TwoId], [t1].[Id0], [t1].[CollectionInverseId0], [t1].[Name0], [t1].[ReferenceInverseId0], [t1].[ThreeId], [t1].[TwoId0] +FROM [EntityOnes] AS [e] +OUTER APPLY ( + SELECT [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t].[OneId], [t].[TwoId], [t0].[Id] AS [Id0], [t0].[CollectionInverseId] AS [CollectionInverseId0], [t0].[Name] AS [Name0], [t0].[ReferenceInverseId] AS [ReferenceInverseId0], [t0].[ThreeId], [t0].[TwoId] AS [TwoId0] + FROM ( + SELECT [e0].[Id], [e0].[CollectionInverseId], [e0].[ExtraId], [e0].[Name], [e0].[ReferenceInverseId], [j].[OneId], [j].[TwoId] + FROM [JoinOneToTwo] AS [j] + INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] + WHERE [e].[Id] = [j].[OneId] + ORDER BY [e0].[Id] + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t] + LEFT JOIN ( + SELECT [e1].[Id], [e1].[CollectionInverseId], [e1].[Name], [e1].[ReferenceInverseId], [j0].[ThreeId], [j0].[TwoId] + FROM [JoinTwoToThree] AS [j0] + INNER JOIN [EntityThrees] AS [e1] ON [j0].[ThreeId] = [e1].[Id] + WHERE [e1].[Id] < 10 + ) AS [t0] ON [t].[Id] = [t0].[TwoId] +) AS [t1] +ORDER BY [e].[Id], [t1].[Id], [t1].[OneId], [t1].[TwoId], [t1].[ThreeId], [t1].[TwoId0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } - diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs index a30acbabfb4..23cc5f7c8e5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs @@ -1038,9 +1038,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(); } @@ -1808,6 +1808,52 @@ FROM [Leaves] AS [l] WHERE 0 = 1"); } + public override async Task Filtered_include_skip_navigation_order_by_take_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_take_EF_Property(async); + + AssertSql( + @"SELECT [e].[Key1], [e].[Key2], [e].[Key3], [e].[Name], [t0].[TwoSkipSharedId], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id], [t0].[CollectionInverseId], [t0].[ExtraId], [t0].[Name], [t0].[ReferenceInverseId] +FROM [EntityCompositeKeys] AS [e] +LEFT JOIN ( + SELECT [t].[TwoSkipSharedId], [t].[CompositeKeySkipSharedKey1], [t].[CompositeKeySkipSharedKey2], [t].[CompositeKeySkipSharedKey3], [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId] + FROM ( + SELECT [e0].[TwoSkipSharedId], [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3], [e1].[Id], [e1].[CollectionInverseId], [e1].[ExtraId], [e1].[Name], [e1].[ReferenceInverseId], ROW_NUMBER() OVER(PARTITION BY [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3] ORDER BY [e1].[Id]) AS [row] + FROM [EntityCompositeKeyEntityTwo] AS [e0] + INNER JOIN [EntityTwos] AS [e1] ON [e0].[TwoSkipSharedId] = [e1].[Id] + ) AS [t] + WHERE [t].[row] <= 2 +) AS [t0] ON [e].[Key1] = [t0].[CompositeKeySkipSharedKey1] AND [e].[Key2] = [t0].[CompositeKeySkipSharedKey2] AND [e].[Key3] = [t0].[CompositeKeySkipSharedKey3] +ORDER BY [e].[Key1], [e].[Key2], [e].[Key3], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id]"); + } + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async); + + AssertSql( + @"SELECT [e].[Id], [e].[Name], [t1].[OneId], [t1].[TwoId], [t1].[JoinOneToTwoExtraId], [t1].[Id], [t1].[CollectionInverseId], [t1].[ExtraId], [t1].[Name], [t1].[ReferenceInverseId], [t1].[ThreeId], [t1].[TwoId0], [t1].[Id0], [t1].[CollectionInverseId0], [t1].[Name0], [t1].[ReferenceInverseId0] +FROM [EntityOnes] AS [e] +OUTER APPLY ( + SELECT [t].[OneId], [t].[TwoId], [t].[JoinOneToTwoExtraId], [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t0].[ThreeId], [t0].[TwoId] AS [TwoId0], [t0].[Id] AS [Id0], [t0].[CollectionInverseId] AS [CollectionInverseId0], [t0].[Name] AS [Name0], [t0].[ReferenceInverseId] AS [ReferenceInverseId0] + FROM ( + SELECT [j].[OneId], [j].[TwoId], [j].[JoinOneToTwoExtraId], [e0].[Id], [e0].[CollectionInverseId], [e0].[ExtraId], [e0].[Name], [e0].[ReferenceInverseId] + FROM [JoinOneToTwo] AS [j] + INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] + WHERE [e].[Id] = [j].[OneId] + ORDER BY [e0].[Id] + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t] + LEFT JOIN ( + SELECT [j0].[ThreeId], [j0].[TwoId], [e1].[Id], [e1].[CollectionInverseId], [e1].[Name], [e1].[ReferenceInverseId] + FROM [JoinTwoToThree] AS [j0] + INNER JOIN [EntityThrees] AS [e1] ON [j0].[ThreeId] = [e1].[Id] + WHERE [e1].[Id] < 10 + ) AS [t0] ON [t].[Id] = [t0].[TwoId] +) AS [t1] +ORDER BY [e].[Id], [t1].[Id], [t1].[OneId], [t1].[TwoId], [t1].[ThreeId], [t1].[TwoId0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 4dbf65492b1..6e627b388af 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -9896,6 +9896,66 @@ FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName]))"); } + public override async Task Include_reference_on_derived_type_using_EF_Property(bool async) + { + await base.Include_reference_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [l].[Name], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l0].[DefeatedByNickname], [l0].[DefeatedBySquadId], [l0].[HighCommandId], CASE + WHEN [l0].[Name] IS NOT NULL THEN N'LocustCommander' +END AS [Discriminator], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator] +FROM [LocustLeaders] AS [l] +LEFT JOIN [LocustCommanders] AS [l0] ON [l].[Name] = [l0].[Name] +LEFT JOIN ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' + END AS [Discriminator] + FROM [Gears] AS [g] + LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] +) AS [t] ON [l0].[DefeatedByNickname] = [t].[Nickname] AND [l0].[DefeatedBySquadId] = [t].[SquadId]"); + } + + public override async Task Include_collection_on_derived_type_using_EF_Property(bool async) + { + await base.Include_collection_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' +END AS [Discriminator], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator] +FROM [Gears] AS [g] +LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] +LEFT JOIN ( + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], CASE + WHEN [o0].[Nickname] IS NOT NULL THEN N'Officer' + END AS [Discriminator] + FROM [Gears] AS [g0] + LEFT JOIN [Officers] AS [o0] ON [g0].[Nickname] = [o0].[Nickname] AND [g0].[SquadId] = [o0].[SquadId] +) AS [t] ON [g].[Nickname] = [t].[LeaderNickname] AND [g].[SquadId] = [t].[LeaderSquadId] +ORDER BY [g].[Nickname], [g].[SquadId], [t].[Nickname]"); + } + + public override async Task EF_Property_based_Include_navigation_on_derived_type(bool async) + { + await base.EF_Property_based_Include_navigation_on_derived_type(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' +END AS [Discriminator], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator] +FROM [Gears] AS [g] +LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] +LEFT JOIN ( + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], CASE + WHEN [o0].[Nickname] IS NOT NULL THEN N'Officer' + END AS [Discriminator] + FROM [Gears] AS [g0] + LEFT JOIN [Officers] AS [o0] ON [g0].[Nickname] = [o0].[Nickname] AND [g0].[SquadId] = [o0].[SquadId] +) AS [t] ON [g].[Nickname] = [t].[LeaderNickname] AND [g].[SquadId] = [t].[LeaderSquadId] +WHERE [o].[Nickname] IS NOT NULL +ORDER BY [g].[Nickname], [g].[SquadId], [t].[Nickname]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs index 3204b77d70b..f82f2423451 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs @@ -1016,9 +1016,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(" "); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(" "); } @@ -1768,6 +1768,52 @@ FROM [Roots] AS [r] WHERE 0 = 1"); } + public override async Task Filtered_include_skip_navigation_order_by_take_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_take_EF_Property(async); + + AssertSql( + @"SELECT [e].[Key1], [e].[Key2], [e].[Key3], [e].[Name], [t0].[Id], [t0].[CollectionInverseId], [t0].[ExtraId], [t0].[Name], [t0].[ReferenceInverseId], [t0].[TwoSkipSharedId], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3] +FROM [EntityCompositeKeys] AS [e] +LEFT JOIN ( + SELECT [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t].[TwoSkipSharedId], [t].[CompositeKeySkipSharedKey1], [t].[CompositeKeySkipSharedKey2], [t].[CompositeKeySkipSharedKey3] + FROM ( + SELECT [e1].[Id], [e1].[CollectionInverseId], [e1].[ExtraId], [e1].[Name], [e1].[ReferenceInverseId], [e0].[TwoSkipSharedId], [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3], ROW_NUMBER() OVER(PARTITION BY [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3] ORDER BY [e1].[Id]) AS [row] + FROM [EntityCompositeKeyEntityTwo] AS [e0] + INNER JOIN [EntityTwos] AS [e1] ON [e0].[TwoSkipSharedId] = [e1].[Id] + ) AS [t] + WHERE [t].[row] <= 2 +) AS [t0] ON [e].[Key1] = [t0].[CompositeKeySkipSharedKey1] AND [e].[Key2] = [t0].[CompositeKeySkipSharedKey2] AND [e].[Key3] = [t0].[CompositeKeySkipSharedKey3] +ORDER BY [e].[Key1], [e].[Key2], [e].[Key3], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id]"); + } + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async); + + AssertSql( + @"SELECT [e].[Id], [e].[Name], [t1].[Id], [t1].[CollectionInverseId], [t1].[ExtraId], [t1].[Name], [t1].[ReferenceInverseId], [t1].[OneId], [t1].[TwoId], [t1].[Id0], [t1].[CollectionInverseId0], [t1].[Name0], [t1].[ReferenceInverseId0], [t1].[ThreeId], [t1].[TwoId0] +FROM [EntityOnes] AS [e] +OUTER APPLY ( + SELECT [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t].[OneId], [t].[TwoId], [t0].[Id] AS [Id0], [t0].[CollectionInverseId] AS [CollectionInverseId0], [t0].[Name] AS [Name0], [t0].[ReferenceInverseId] AS [ReferenceInverseId0], [t0].[ThreeId], [t0].[TwoId] AS [TwoId0] + FROM ( + SELECT [e0].[Id], [e0].[CollectionInverseId], [e0].[ExtraId], [e0].[Name], [e0].[ReferenceInverseId], [j].[OneId], [j].[TwoId] + FROM [JoinOneToTwo] AS [j] + INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] + WHERE [e].[Id] = [j].[OneId] + ORDER BY [e0].[Id] + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t] + LEFT JOIN ( + SELECT [e1].[Id], [e1].[CollectionInverseId], [e1].[Name], [e1].[ReferenceInverseId], [j0].[ThreeId], [j0].[TwoId] + FROM [JoinTwoToThree] AS [j0] + INNER JOIN [EntityThrees] AS [e1] ON [j0].[ThreeId] = [e1].[Id] + WHERE [e1].[Id] < 10 + ) AS [t0] ON [t].[Id] = [t0].[TwoId] +) AS [t1] +ORDER BY [e].[Id], [t1].[Id], [t1].[OneId], [t1].[TwoId], [t1].[ThreeId], [t1].[TwoId0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs index a1acbdcb09d..4ee21ee5da9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs @@ -1015,9 +1015,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool AssertSql(" "); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql(" "); } @@ -1772,6 +1772,52 @@ FROM [Roots] AS [r] WHERE 0 = 1"); } + public override async Task Filtered_include_skip_navigation_order_by_take_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_take_EF_Property(async); + + AssertSql( + @"SELECT [e].[Key1], [e].[Key2], [e].[Key3], [e].[Name], [t0].[TwoSkipSharedId], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id], [t0].[CollectionInverseId], [t0].[ExtraId], [t0].[Name], [t0].[ReferenceInverseId] +FROM [EntityCompositeKeys] AS [e] +LEFT JOIN ( + SELECT [t].[TwoSkipSharedId], [t].[CompositeKeySkipSharedKey1], [t].[CompositeKeySkipSharedKey2], [t].[CompositeKeySkipSharedKey3], [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId] + FROM ( + SELECT [e0].[TwoSkipSharedId], [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3], [e1].[Id], [e1].[CollectionInverseId], [e1].[ExtraId], [e1].[Name], [e1].[ReferenceInverseId], ROW_NUMBER() OVER(PARTITION BY [e0].[CompositeKeySkipSharedKey1], [e0].[CompositeKeySkipSharedKey2], [e0].[CompositeKeySkipSharedKey3] ORDER BY [e1].[Id]) AS [row] + FROM [EntityCompositeKeyEntityTwo] AS [e0] + INNER JOIN [EntityTwos] AS [e1] ON [e0].[TwoSkipSharedId] = [e1].[Id] + ) AS [t] + WHERE [t].[row] <= 2 +) AS [t0] ON [e].[Key1] = [t0].[CompositeKeySkipSharedKey1] AND [e].[Key2] = [t0].[CompositeKeySkipSharedKey2] AND [e].[Key3] = [t0].[CompositeKeySkipSharedKey3] +ORDER BY [e].[Key1], [e].[Key2], [e].[Key3], [t0].[CompositeKeySkipSharedKey1], [t0].[CompositeKeySkipSharedKey2], [t0].[CompositeKeySkipSharedKey3], [t0].[Id]"); + } + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(bool async) + { + await base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async); + + AssertSql( + @"SELECT [e].[Id], [e].[Name], [t1].[OneId], [t1].[TwoId], [t1].[JoinOneToTwoExtraId], [t1].[Id], [t1].[CollectionInverseId], [t1].[ExtraId], [t1].[Name], [t1].[ReferenceInverseId], [t1].[ThreeId], [t1].[TwoId0], [t1].[Id0], [t1].[CollectionInverseId0], [t1].[Name0], [t1].[ReferenceInverseId0] +FROM [EntityOnes] AS [e] +OUTER APPLY ( + SELECT [t].[OneId], [t].[TwoId], [t].[JoinOneToTwoExtraId], [t].[Id], [t].[CollectionInverseId], [t].[ExtraId], [t].[Name], [t].[ReferenceInverseId], [t0].[ThreeId], [t0].[TwoId] AS [TwoId0], [t0].[Id] AS [Id0], [t0].[CollectionInverseId] AS [CollectionInverseId0], [t0].[Name] AS [Name0], [t0].[ReferenceInverseId] AS [ReferenceInverseId0] + FROM ( + SELECT [j].[OneId], [j].[TwoId], [j].[JoinOneToTwoExtraId], [e0].[Id], [e0].[CollectionInverseId], [e0].[ExtraId], [e0].[Name], [e0].[ReferenceInverseId] + FROM [JoinOneToTwo] AS [j] + INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] + WHERE [e].[Id] = [j].[OneId] + ORDER BY [e0].[Id] + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [t] + LEFT JOIN ( + SELECT [j0].[ThreeId], [j0].[TwoId], [e1].[Id], [e1].[CollectionInverseId], [e1].[Name], [e1].[ReferenceInverseId] + FROM [JoinTwoToThree] AS [j0] + INNER JOIN [EntityThrees] AS [e1] ON [j0].[ThreeId] = [e1].[Id] + WHERE [e1].[Id] < 10 + ) AS [t0] ON [t].[Id] = [t0].[TwoId] +) AS [t1] +ORDER BY [e].[Id], [t1].[Id], [t1].[OneId], [t1].[TwoId], [t1].[ThreeId], [t1].[TwoId0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs index 76e00e65854..d4d30f74d12 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -2464,6 +2464,125 @@ GROUP BY [l2].[Name] ORDER BY [l].[Id], [l0].[Id], [l1].[Id]"); } + public override async Task Multiple_complex_includes_EF_Property(bool async) + { + await base.Multiple_complex_includes_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l0].[PeriodEnd], [l0].[PeriodStart], [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], [l1].[PeriodEnd], [l1].[PeriodStart], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[PeriodEnd], [t].[PeriodStart], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t].[PeriodEnd0], [t].[PeriodStart0] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +LEFT JOIN [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [l2].[PeriodEnd], [l2].[PeriodStart], [l3].[Id] AS [Id0], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id], [l3].[PeriodEnd] AS [PeriodEnd0], [l3].[PeriodStart] AS [PeriodStart0] + FROM [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l2] + LEFT JOIN [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l3] ON [l2].[Id] = [l3].[Level2_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Id], [l1].[Id], [t].[Id]"); + } + + public override async Task Multiple_complex_includes_self_ref_EF_Property(bool async) + { + await base.Multiple_complex_includes_self_ref_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [l0].[Id], [l0].[Date], [l0].[Name], [l0].[OneToMany_Optional_Self_Inverse1Id], [l0].[OneToMany_Required_Self_Inverse1Id], [l0].[OneToOne_Optional_Self1Id], [l0].[PeriodEnd], [l0].[PeriodStart], [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_Inverse1Id], [l1].[OneToMany_Required_Self_Inverse1Id], [l1].[OneToOne_Optional_Self1Id], [l1].[PeriodEnd], [l1].[PeriodStart], [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [t].[PeriodEnd], [t].[PeriodStart], [t].[Id0], [t].[Date0], [t].[Name0], [t].[OneToMany_Optional_Self_Inverse1Id0], [t].[OneToMany_Required_Self_Inverse1Id0], [t].[OneToOne_Optional_Self1Id0], [t].[PeriodEnd0], [t].[PeriodStart0] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] ON [l].[OneToOne_Optional_Self1Id] = [l0].[Id] +LEFT JOIN [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Self_Inverse1Id] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Name], [l2].[OneToMany_Optional_Self_Inverse1Id], [l2].[OneToMany_Required_Self_Inverse1Id], [l2].[OneToOne_Optional_Self1Id], [l2].[PeriodEnd], [l2].[PeriodStart], [l3].[Id] AS [Id0], [l3].[Date] AS [Date0], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Self_Inverse1Id] AS [OneToMany_Optional_Self_Inverse1Id0], [l3].[OneToMany_Required_Self_Inverse1Id] AS [OneToMany_Required_Self_Inverse1Id0], [l3].[OneToOne_Optional_Self1Id] AS [OneToOne_Optional_Self1Id0], [l3].[PeriodEnd] AS [PeriodEnd0], [l3].[PeriodStart] AS [PeriodStart0] + FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l2] + LEFT JOIN [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l3] ON [l2].[OneToOne_Optional_Self1Id] = [l3].[Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Self_Inverse1Id] +ORDER BY [l].[Id], [l0].[Id], [l1].[Id], [t].[Id]"); + } + + public override async Task Include_collection_multiple_with_filter_EF_Property(bool async) + { + await base.Include_collection_multiple_with_filter_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[PeriodEnd], [t].[PeriodStart], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t].[PeriodEnd0], [t].[PeriodStart0], [t].[Id1], [t].[Level3_Optional_Id], [t].[Level3_Required_Id], [t].[Name1], [t].[OneToMany_Optional_Inverse4Id], [t].[OneToMany_Optional_Self_Inverse4Id], [t].[OneToMany_Required_Inverse4Id], [t].[OneToMany_Required_Self_Inverse4Id], [t].[OneToOne_Optional_PK_Inverse4Id], [t].[OneToOne_Optional_Self4Id], [t].[PeriodEnd1], [t].[PeriodStart1] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN ( + SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [l2].[PeriodEnd], [l2].[PeriodStart], [l3].[Id] AS [Id0], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name] AS [Name0], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id], [l3].[PeriodEnd] AS [PeriodEnd0], [l3].[PeriodStart] AS [PeriodStart0], [l4].[Id] AS [Id1], [l4].[Level3_Optional_Id], [l4].[Level3_Required_Id], [l4].[Name] AS [Name1], [l4].[OneToMany_Optional_Inverse4Id], [l4].[OneToMany_Optional_Self_Inverse4Id], [l4].[OneToMany_Required_Inverse4Id], [l4].[OneToMany_Required_Self_Inverse4Id], [l4].[OneToOne_Optional_PK_Inverse4Id], [l4].[OneToOne_Optional_Self4Id], [l4].[PeriodEnd] AS [PeriodEnd1], [l4].[PeriodStart] AS [PeriodStart1] + FROM [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l2] + LEFT JOIN [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l3] ON [l2].[Id] = [l3].[OneToOne_Optional_PK_Inverse3Id] + LEFT JOIN [LevelFour] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l4] ON [l3].[Id] = [l4].[Level3_Optional_Id] +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +WHERE ( + SELECT COUNT(*) + FROM [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] + LEFT JOIN [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l1] ON [l0].[Id] = [l1].[OneToOne_Optional_PK_Inverse3Id] + WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] AND ([l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL)) > 0 +ORDER BY [l].[Id], [t].[Id], [t].[Id0]"); + } + + public override async Task Filtered_include_basic_Where_EF_Property(bool async) + { + await base.Filtered_include_basic_Where_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[PeriodEnd], [t].[PeriodStart] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l0].[PeriodEnd], [l0].[PeriodStart] + FROM [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] + WHERE [l0].[Id] > 5 +) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id]"); + } + + public override async Task Filtered_include_OrderBy_EF_Property(bool async) + { + await base.Filtered_include_OrderBy_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l0].[PeriodEnd], [l0].[PeriodStart] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] ON [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [l0].[Name]"); + } + + public override async Task Filtered_include_basic_OrderBy_Skip_Take_EF_Property(bool async) + { + await base.Filtered_include_basic_OrderBy_Skip_Take_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id], [t0].[PeriodEnd], [t0].[PeriodStart] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN ( + SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[PeriodEnd], [t].[PeriodStart] + FROM ( + SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l0].[PeriodEnd], [l0].[PeriodStart], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Name]) AS [row] + FROM [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +ORDER BY [l].[Id], [t0].[OneToMany_Optional_Inverse2Id], [t0].[Name]"); + } + + public override async Task Filtered_include_on_ThenInclude_EF_Property(bool async) + { + await base.Filtered_include_on_ThenInclude_EF_Property(async); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l].[PeriodEnd], [l].[PeriodStart], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l0].[PeriodEnd], [l0].[PeriodStart], [t0].[Id], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [t0].[PeriodEnd], [t0].[PeriodStart] +FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t].[PeriodEnd], [t].[PeriodStart] + FROM ( + SELECT [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], [l1].[PeriodEnd], [l1].[PeriodStart], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Optional_Inverse3Id] ORDER BY [l1].[Name]) AS [row] + FROM [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l1] + WHERE [l1].[Name] <> N'Foo' OR [l1].[Name] IS NULL + ) AS [t] + WHERE 1 < [t].[row] AND [t].[row] <= 4 +) AS [t0] ON [l0].[Id] = [t0].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [l0].[Id], [t0].[OneToMany_Optional_Inverse3Id], [t0].[Name]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs index fb1f557ca74..df3ec61adfc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs @@ -3015,10 +3015,7 @@ public override async Task SelectMany_with_predicate_and_DefaultIfEmpty_projecti var exception = await Assert.ThrowsAsync(() => base.SelectMany_with_predicate_and_DefaultIfEmpty_projecting_root_collection_element_and_another_collection(async)); - Assert.Equal( - CoreStrings.ExpressionParameterizationExceptionSensitive( - "Convert(value(Microsoft.EntityFrameworkCore.Query.ComplexNavigationsCollectionsQueryTestBase`1+<>c__DisplayClass140_0[Microsoft.EntityFrameworkCore.Query.TemporalComplexNavigationsSharedTypeQuerySqlServerFixture]).ss.Set(), DbSet`1).TemporalAsOf(1/1/2010 12:00:00 AM)"), - exception.Message); + Assert.StartsWith(CoreStrings.ExpressionParameterizationExceptionSensitive("X").Substring(0, 30), exception.Message); Assert.True(exception.InnerException is InvalidCastException); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index 534be2fb092..4821c01de84 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -8515,6 +8515,39 @@ SELECT 1 WHERE [g].[FullName] = [w].[OwnerFullName]))"); } + public override async Task Include_reference_on_derived_type_using_EF_Property(bool async) + { + await base.Include_reference_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[PeriodEnd], [l].[PeriodStart], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId], [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] +FROM [LocustLeaders] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] +LEFT JOIN [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] ON [l].[DefeatedByNickname] = [g].[Nickname] AND [l].[DefeatedBySquadId] = [g].[SquadId]"); + } + + public override async Task Include_collection_on_derived_type_using_EF_Property(bool async) + { + await base.Include_collection_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank], [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[PeriodEnd], [g0].[PeriodStart], [g0].[Rank] +FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] +LEFT JOIN [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g0] ON [g].[Nickname] = [g0].[LeaderNickname] AND [g].[SquadId] = [g0].[LeaderSquadId] +ORDER BY [g].[Nickname], [g].[SquadId], [g0].[Nickname]"); + } + + public override async Task EF_Property_based_Include_navigation_on_derived_type(bool async) + { + await base.EF_Property_based_Include_navigation_on_derived_type(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank], [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[PeriodEnd], [g0].[PeriodStart], [g0].[Rank] +FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] +LEFT JOIN [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g0] ON [g].[Nickname] = [g0].[LeaderNickname] AND [g].[SquadId] = [g0].[LeaderSquadId] +WHERE [g].[Discriminator] = N'Officer' +ORDER BY [g].[Nickname], [g].[SquadId], [g0].[Nickname]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs index bdb19105c30..d7efe4be2ba 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs @@ -1002,9 +1002,9 @@ public override async Task Includes_accessed_via_different_path_are_merged(bool @""); } - public override async Task Filered_includes_accessed_via_different_path_are_merged(bool async) + public override async Task Filtered_includes_accessed_via_different_path_are_merged(bool async) { - await base.Filered_includes_accessed_via_different_path_are_merged(async); + await base.Filtered_includes_accessed_via_different_path_are_merged(async); AssertSql( @""); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index 65edd9c7ec5..fcbf37b9669 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -8106,6 +8106,39 @@ SELECT 1 WHERE ""g"".""FullName"" = ""w"".""OwnerFullName""))"); } + public override async Task Include_reference_on_derived_type_using_EF_Property(bool async) + { + await base.Include_reference_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT ""l"".""Name"", ""l"".""Discriminator"", ""l"".""LocustHordeId"", ""l"".""ThreatLevel"", ""l"".""ThreatLevelByte"", ""l"".""ThreatLevelNullableByte"", ""l"".""DefeatedByNickname"", ""l"".""DefeatedBySquadId"", ""l"".""HighCommandId"", ""g"".""Nickname"", ""g"".""SquadId"", ""g"".""AssignedCityName"", ""g"".""CityOfBirthName"", ""g"".""Discriminator"", ""g"".""FullName"", ""g"".""HasSoulPatch"", ""g"".""LeaderNickname"", ""g"".""LeaderSquadId"", ""g"".""Rank"" +FROM ""LocustLeaders"" AS ""l"" +LEFT JOIN ""Gears"" AS ""g"" ON ""l"".""DefeatedByNickname"" = ""g"".""Nickname"" AND ""l"".""DefeatedBySquadId"" = ""g"".""SquadId"""); + } + + public override async Task Include_collection_on_derived_type_using_EF_Property(bool async) + { + await base.Include_collection_on_derived_type_using_EF_Property(async); + + AssertSql( + @"SELECT ""g"".""Nickname"", ""g"".""SquadId"", ""g"".""AssignedCityName"", ""g"".""CityOfBirthName"", ""g"".""Discriminator"", ""g"".""FullName"", ""g"".""HasSoulPatch"", ""g"".""LeaderNickname"", ""g"".""LeaderSquadId"", ""g"".""Rank"", ""g0"".""Nickname"", ""g0"".""SquadId"", ""g0"".""AssignedCityName"", ""g0"".""CityOfBirthName"", ""g0"".""Discriminator"", ""g0"".""FullName"", ""g0"".""HasSoulPatch"", ""g0"".""LeaderNickname"", ""g0"".""LeaderSquadId"", ""g0"".""Rank"" +FROM ""Gears"" AS ""g"" +LEFT JOIN ""Gears"" AS ""g0"" ON ""g"".""Nickname"" = ""g0"".""LeaderNickname"" AND ""g"".""SquadId"" = ""g0"".""LeaderSquadId"" +ORDER BY ""g"".""Nickname"", ""g"".""SquadId"", ""g0"".""Nickname"""); + } + + public override async Task EF_Property_based_Include_navigation_on_derived_type(bool async) + { + await base.EF_Property_based_Include_navigation_on_derived_type(async); + + AssertSql( + @"SELECT ""g"".""Nickname"", ""g"".""SquadId"", ""g"".""AssignedCityName"", ""g"".""CityOfBirthName"", ""g"".""Discriminator"", ""g"".""FullName"", ""g"".""HasSoulPatch"", ""g"".""LeaderNickname"", ""g"".""LeaderSquadId"", ""g"".""Rank"", ""g0"".""Nickname"", ""g0"".""SquadId"", ""g0"".""AssignedCityName"", ""g0"".""CityOfBirthName"", ""g0"".""Discriminator"", ""g0"".""FullName"", ""g0"".""HasSoulPatch"", ""g0"".""LeaderNickname"", ""g0"".""LeaderSquadId"", ""g0"".""Rank"" +FROM ""Gears"" AS ""g"" +LEFT JOIN ""Gears"" AS ""g0"" ON ""g"".""Nickname"" = ""g0"".""LeaderNickname"" AND ""g"".""SquadId"" = ""g0"".""LeaderSquadId"" +WHERE ""g"".""Discriminator"" = 'Officer' +ORDER BY ""g"".""Nickname"", ""g"".""SquadId"", ""g0"".""Nickname"""); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyNoTrackingQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyNoTrackingQuerySqliteTest.cs index fcda821b8e8..4b94cb83a1f 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyNoTrackingQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyNoTrackingQuerySqliteTest.cs @@ -26,4 +26,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyQuerySqliteTest.cs index 4302b84b47e..a29e5deb800 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ManyToManyQuerySqliteTest.cs @@ -23,4 +23,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqliteTest.cs new file mode 100644 index 00000000000..7d64b7936fd --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqliteTest.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Sqlite.Internal; + +namespace Microsoft.EntityFrameworkCore.Query; + +public class NorthwindEFPropertyIncludeQuerySqliteTest : NorthwindEFPropertyIncludeQueryTestBase< + NorthwindQuerySqliteFixture> +{ + public NorthwindEFPropertyIncludeQuerySqliteTest( + NorthwindQuerySqliteFixture fixture, + ITestOutputHelper testOutputHelper) + : base(fixture) + { + //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); + } + + public override async Task Filtered_include_with_multiple_ordering(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_with_multiple_ordering(async))).Message); + + public override async Task Include_collection_with_cross_apply_with_filter(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Include_collection_with_cross_apply_with_filter(async))).Message); + + public override async Task Include_collection_with_outer_apply_with_filter(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Include_collection_with_outer_apply_with_filter(async))).Message); + + public override async Task Include_collection_with_outer_apply_with_filter_non_equality(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Include_collection_with_outer_apply_with_filter_non_equality(async))).Message); + + public override async Task Include_collection_with_last_no_orderby(bool async) + => Assert.Equal( + RelationalStrings.LastUsedWithoutOrderBy(nameof(Enumerable.Last)), + (await Assert.ThrowsAsync( + () => base.Include_collection_with_last_no_orderby(async))).Message); +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqliteTest.cs index cf641bd446c..dda1c61aec5 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqliteTest.cs @@ -23,4 +23,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyQuerySqliteTest.cs index 6d7a5f16f76..9852d8d0b1c 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPCManyToManyQuerySqliteTest.cs @@ -23,4 +23,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqliteTest.cs index 8bd2ffc746a..e4d846835c9 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqliteTest.cs @@ -23,4 +23,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyQuerySqliteTest.cs index fb40685aef4..a9c4613c6e6 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPTManyToManyQuerySqliteTest.cs @@ -23,4 +23,12 @@ public override async Task Filtered_include_skip_navigation_order_by_skip_take_t SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where(async))).Message); + + public override async Task Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property( + bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Filtered_include_skip_navigation_order_by_skip_take_then_include_skip_navigation_where_EF_Property(async))) + .Message); }