From fc3392334fde47762b507249925bde54d347d14e Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 11 Jun 2020 23:47:07 -0700 Subject: [PATCH] Add tests for split include and no tracking (#21218) Part of #20892 --- ...windSplitIncludeNoTrackingQueryTestBase.cs | 159 ++++++++++++++++++ .../NorthwindSplitIncludeQueryTestBase.cs | 12 +- ...NorthwindIncludeNoTrackingQueryTestBase.cs | 1 - ...plitIncludeNoTrackingQuerySqlServerTest.cs | 36 ++++ ...ndSplitIncludeNoTrackingQuerySqliteTest.cs | 23 +++ 5 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeNoTrackingQueryTestBase.cs create mode 100644 test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs create mode 100644 test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqliteTest.cs diff --git a/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeNoTrackingQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeNoTrackingQueryTestBase.cs new file mode 100644 index 00000000000..249aff4ed19 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeNoTrackingQueryTestBase.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.TestModels.Northwind; +using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; + +// ReSharper disable FormatStringProblem +// ReSharper disable InconsistentNaming +// ReSharper disable ConvertToConstant.Local +// ReSharper disable AccessToDisposedClosure +namespace Microsoft.EntityFrameworkCore.Query +{ + public abstract class NorthwindSplitIncludeNoTrackingQueryTestBase : NorthwindIncludeNoTrackingQueryTestBase + where TFixture : NorthwindQueryFixtureBase, new() + { + private static readonly MethodInfo _asSplitIncludeMethodInfo + = typeof(RelationalQueryableExtensions) + .GetTypeInfo().GetDeclaredMethod(nameof(RelationalQueryableExtensions.AsSplitQuery)); + + protected NorthwindSplitIncludeNoTrackingQueryTestBase(TFixture fixture) + : base(fixture) + { + } + + public override async Task Include_closes_reader(bool async) + { + using var context = CreateContext(); + if (async) + { + Assert.NotNull(await context.Set().Include(c => c.Orders).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync()); + Assert.NotNull(await context.Set().AsNoTracking().ToListAsync()); + } + else + { + Assert.NotNull(context.Set().Include(c => c.Orders).AsNoTracking().AsSplitQuery().FirstOrDefault()); + Assert.NotNull(context.Set().AsNoTracking().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()); + Assert.True(orders.All(o => o.Customer == null)); + + var customer + = async + ? await context.Set() + .Include(c => c.Orders) + .AsSplitQuery() + .AsNoTracking() + .SingleAsync(c => c.CustomerID == "ALFKI") + : context.Set() + .Include(c => c.Orders) + .AsSplitQuery() + .AsNoTracking() + .Single(c => c.CustomerID == "ALFKI"); + + Assert.NotEqual(orders, customer.Orders, LegacyReferenceEqualityComparer.Instance); + Assert.Equal(6, customer.Orders.Count); + Assert.True(customer.Orders.All(e => ReferenceEquals(e.Customer, customer))); + + Assert.Equal(6, context.ChangeTracker.Entries().Count()); + Assert.True(orders.All(o => o.Customer == null)); + } + + 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 => c.Orders) + .AsSplitQuery() + .AsNoTracking() + .SingleAsync(c => c.CustomerID == "ALFKI") + : context.Set() + .Include(c => c.Orders) + .AsSplitQuery() + .AsNoTracking() + .Single(c => c.CustomerID == "ALFKI"); + + Assert.NotSame(customer1, customer2); + Assert.Equal(6, customer2.Orders.Count); + Assert.True(customer2.Orders.All(o => o.Customer != null)); + Assert.True(customer2.Orders.All(o => ReferenceEquals(o.Customer, customer2))); + + Assert.Single(context.ChangeTracker.Entries()); + } + + 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 => o.Customer).AsNoTracking().AsSplitQuery().Where(o => o.CustomerID == "ALFKI").ToListAsync() + : context.Set().Include(o => o.Customer).AsNoTracking().AsSplitQuery().Where(o => o.CustomerID == "ALFKI").ToList(); + + Assert.Equal(6, orders.Count); + Assert.True(orders.All(o => !ReferenceEquals(o.Customer, customer))); + Assert.True(orders.All(o => o.Customer != null)); + Assert.Single(context.ChangeTracker.Entries()); + } + + public override async Task Include_collection_with_last_no_orderby(bool async) + { + Assert.Equal( + CoreStrings.TranslationFailed("DbSet() .Reverse()"), + (await Assert.ThrowsAsync( + () => AssertLast( + async, + ss => ss.Set().Include(c => c.Orders), + entryCount: 8))).Message.Replace("\r", "").Replace("\n", "")); + } + + [ConditionalTheory(Skip = "Collection Include on nested collection")] + public override Task Multi_level_includes_are_applied_with_take(bool async) + { + return base.Multi_level_includes_are_applied_with_take(async); + } + + [ConditionalTheory(Skip = "Collection Include on nested collection")] + public override Task Multi_level_includes_are_applied_with_skip(bool async) + { + return base.Multi_level_includes_are_applied_with_skip(async); + } + + [ConditionalTheory(Skip = "Collection Include on nested collection")] + public override Task Multi_level_includes_are_applied_with_skip_take(bool async) + { + return base.Multi_level_includes_are_applied_with_skip_take(async); + } + + protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression) + { + serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression); + + return Expression.Call( + _asSplitIncludeMethodInfo.MakeGenericMethod(serverQueryExpression.Type.TryGetSequenceType()), + serverQueryExpression); + } + } +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeQueryTestBase.cs index 0c5b0b484e4..b3775ba83e6 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NorthwindSplitIncludeQueryTestBase.cs @@ -36,12 +36,12 @@ public override async Task Include_closes_reader(bool async) using var context = CreateContext(); if (async) { - Assert.NotNull(await context.Set().Include(c => c.Orders).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync()); + Assert.NotNull(await context.Set().Include(c => c.Orders).AsSplitQuery().FirstOrDefaultAsync()); Assert.NotNull(await context.Set().AsNoTracking().ToListAsync()); } else { - Assert.NotNull(context.Set().Include(c => c.Orders).AsNoTracking().AsSplitQuery().FirstOrDefault()); + Assert.NotNull(context.Set().Include(c => c.Orders).AsSplitQuery().FirstOrDefault()); Assert.NotNull(context.Set().AsNoTracking().ToList()); } } @@ -115,23 +115,23 @@ public override async Task Include_collection_with_last_no_orderby(bool async) (await Assert.ThrowsAsync( () => AssertLast( async, - ss => ss.Set().Include(c => c.Orders).AsSplitQuery(), + ss => ss.Set().Include(c => c.Orders), entryCount: 8))).Message.Replace("\r","").Replace("\n","")); } - [ConditionalTheory(Skip = "Nested collection include")] + [ConditionalTheory(Skip = "Collection Include on nested collection")] public override Task Multi_level_includes_are_applied_with_take(bool async) { return base.Multi_level_includes_are_applied_with_take(async); } - [ConditionalTheory(Skip = "Nested collection include")] + [ConditionalTheory(Skip = "Collection Include on nested collection")] public override Task Multi_level_includes_are_applied_with_skip(bool async) { return base.Multi_level_includes_are_applied_with_skip(async); } - [ConditionalTheory(Skip = "Nested collection include")] + [ConditionalTheory(Skip = "Collection Include on nested collection")] public override Task Multi_level_includes_are_applied_with_skip_take(bool async) { return base.Multi_level_includes_are_applied_with_skip_take(async); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeNoTrackingQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeNoTrackingQueryTestBase.cs index a81d8df4a7d..a879bf9bd18 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeNoTrackingQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeNoTrackingQueryTestBase.cs @@ -189,7 +189,6 @@ var customer2 Assert.NotSame(customer1, customer2); Assert.Equal(6, customer2.Orders.Count); Assert.True(customer2.Orders.All(o => o.Customer != null)); - Assert.True(customer2.Orders.All(o => !ReferenceEquals(o.Customer, customer1))); Assert.True(customer2.Orders.All(o => ReferenceEquals(o.Customer, customer2))); Assert.Single(context.ChangeTracker.Entries()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs new file mode 100644 index 00000000000..17d50c92806 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit.Abstractions; + +namespace Microsoft.EntityFrameworkCore.Query +{ + public class NorthwindSplitIncludeNoTrackingQuerySqlServerTest : NorthwindSplitIncludeNoTrackingQueryTestBase + { + // ReSharper disable once UnusedParameter.Local + public NorthwindSplitIncludeNoTrackingQuerySqlServerTest(NorthwindQuerySqlServerMARSFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public class NorthwindQuerySqlServerMARSFixture : NorthwindQuerySqlServerFixture + { + protected override ITestStoreFactory TestStoreFactory => SqlServerNorthwindMARSTestStoreFactory.Instance; + } + + private class SqlServerNorthwindMARSTestStoreFactory : SqlServerNorthwindTestStoreFactory + { + public static new SqlServerNorthwindMARSTestStoreFactory Instance { get; } = new SqlServerNorthwindMARSTestStoreFactory(); + + protected SqlServerNorthwindMARSTestStoreFactory() + { + } + + public override TestStore GetOrCreate(string storeName) + => SqlServerTestStore.GetOrCreate(Name, "Northwind.sql", multipleActiveResultSets: true); + } + } +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqliteTest.cs new file mode 100644 index 00000000000..7c99d907487 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqliteTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit.Abstractions; + +namespace Microsoft.EntityFrameworkCore.Query +{ + public class NorthwindSplitIncludeNoTrackingQuerySqliteTest : NorthwindSplitIncludeNoTrackingQueryTestBase> + { + public NorthwindSplitIncludeNoTrackingQuerySqliteTest(NorthwindQuerySqliteFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); + } + + // Sqlite does not support Apply operations + public override Task Include_collection_with_cross_apply_with_filter(bool async) => Task.CompletedTask; + + public override Task Include_collection_with_outer_apply_with_filter(bool async) => Task.CompletedTask; + } +}