From 88f0ba8df5c72c72aa5105399547653231b84d7f Mon Sep 17 00:00:00 2001 From: Christopher Jolly Date: Mon, 23 Oct 2023 01:47:11 +0800 Subject: [PATCH] Fixes to the QueryBugsTest (#162) * Ensure connection is closed before dropping the database/deleting the file * Upgrade the QueryBugsTest --- .../Query/QueryBugsTest.cs | 13359 ++++++++++------ .../TestUtilities/JetTestHelpers.cs | 2 +- .../TestUtilities/JetTestStore.cs | 8 +- 3 files changed, 8192 insertions(+), 5177 deletions(-) diff --git a/test/EFCore.Jet.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.Jet.FunctionalTests/Query/QueryBugsTest.cs index f33dee7..130c3a0 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/QueryBugsTest.cs @@ -12,7 +12,6 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities; -using EntityFrameworkCore.Jet.Data; using System.Data.OleDb; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -21,9 +20,13 @@ using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; using Xunit; using Xunit.Abstractions; +using EntityFrameworkCore.Jet.Infrastructure; +using System.Threading; +using EntityFrameworkCore.Jet.Infrastructure.Internal; +using System.Globalization; +using Microsoft.EntityFrameworkCore.Diagnostics.Internal; #pragma warning disable IDE0063 // Use simple 'using' statement @@ -35,49 +38,37 @@ using Xunit.Abstractions; // ReSharper disable UnusedMember.Local namespace EntityFrameworkCore.Jet.FunctionalTests.Query { - public class QueryBugsTest : IClassFixture + public class QueryBugsTest : NonSharedModelTestBase { - // ReSharper disable once UnusedParameter.Local -#pragma warning disable IDE0060 // Remove unused parameter - public QueryBugsTest(JetFixture fixture, ITestOutputHelper testOutputHelper) -#pragma warning restore IDE0060 // Remove unused parameter + public QueryBugsTest(ITestOutputHelper testOutputHelper) { - Fixture = fixture; - Fixture.TestSqlLoggerFactory.Clear(); - //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - protected JetFixture Fixture { get; } + #region Issue14095 [ConditionalTheory] [InlineData(false)] [InlineData(true)] public async Task Where_equals_DateTime_Now(bool async) { - using (CreateDateTimeStore()) - { - Fixture.TestSqlLoggerFactory.Clear(); + var contextFactory = await InitializeDateTimeContextAsync(); - using (var context = new DateTimeContext(_options)) - { - var query = context.Dates.Where( - d => d.DateTime2_2 == DateTime.Now - || d.DateTime2_7 == DateTime.Now - || d.DateTime == DateTime.Now - || d.SmallDateTime == DateTime.Now); + using var context = contextFactory.CreateContext(); + var query = context.Dates.Where( + d => d.DateTime == DateTime.Now); - var results = async - ? await query.ToListAsync() - : query.ToList(); + var results = async + ? await query.ToListAsync() + : query.ToList(); - Assert.Empty(results); + Assert.Empty(results); - AssertSql( - $@"SELECT `d`.`Id`, `d`.`DateTime`, `d`.`DateTime2`, `d`.`DateTime2_0`, `d`.`DateTime2_1`, `d`.`DateTime2_2`, `d`.`DateTime2_3`, `d`.`DateTime2_4`, `d`.`DateTime2_5`, `d`.`DateTime2_6`, `d`.`DateTime2_7`, `d`.`SmallDateTime` + AssertSql( +""" +SELECT `d`.`Id`, `d`.`DateTime` FROM `Dates` AS `d` -WHERE (((`d`.`DateTime2_2` = GETDATE()) OR (`d`.`DateTime2_7` = GETDATE())) OR (`d`.`DateTime` = GETDATE())) OR (`d`.`SmallDateTime` = GETDATE())"); - } - } +WHERE `d`.`DateTime` = NOW() +"""); } [ConditionalTheory] @@ -85,30 +76,24 @@ WHERE (((`d`.`DateTime2_2` = GETDATE()) OR (`d`.`DateTime2_7` = GETDATE())) OR ( [InlineData(true)] public async Task Where_not_equals_DateTime_Now(bool async) { - using (CreateDateTimeStore()) - { - Fixture.TestSqlLoggerFactory.Clear(); + var contextFactory = await InitializeDateTimeContextAsync(); - using (var context = new DateTimeContext(_options)) - { - var query = context.Dates.Where( - d => d.DateTime2_2 != DateTime.Now - && d.DateTime2_7 != DateTime.Now - && d.DateTime != DateTime.Now - && d.SmallDateTime != DateTime.Now); + using var context = contextFactory.CreateContext(); + var query = context.Dates.Where( + d => d.DateTime != DateTime.Now); - var results = async - ? await query.ToListAsync() - : query.ToList(); + var results = async + ? await query.ToListAsync() + : query.ToList(); - Assert.Single(results); + Assert.Single(results); - AssertSql( - $@"SELECT `d`.`Id`, `d`.`DateTime`, `d`.`DateTime2`, `d`.`DateTime2_0`, `d`.`DateTime2_1`, `d`.`DateTime2_2`, `d`.`DateTime2_3`, `d`.`DateTime2_4`, `d`.`DateTime2_5`, `d`.`DateTime2_6`, `d`.`DateTime2_7`, `d`.`SmallDateTime` + AssertSql( +""" +SELECT `d`.`Id`, `d`.`DateTime` FROM `Dates` AS `d` -WHERE ((((`d`.`DateTime2_2` <> GETDATE()) OR GETDATE() IS NULL) AND ((`d`.`DateTime2_7` <> GETDATE()) OR GETDATE() IS NULL)) AND ((`d`.`DateTime` <> GETDATE()) OR GETDATE() IS NULL)) AND ((`d`.`SmallDateTime` <> GETDATE()) OR GETDATE() IS NULL)"); - } - } +WHERE `d`.`DateTime` <> NOW() +"""); } [ConditionalTheory] @@ -116,37 +101,24 @@ WHERE ((((`d`.`DateTime2_2` <> GETDATE()) OR GETDATE() IS NULL) AND ((`d`.`DateT [InlineData(true)] public async Task Where_equals_new_DateTime(bool async) { - using (CreateDateTimeStore()) - { - Fixture.TestSqlLoggerFactory.Clear(); + var contextFactory = await InitializeDateTimeContextAsync(); - using (var context = new DateTimeContext(_options)) - { - var query = context.Dates.Where( - d => d.SmallDateTime == new DateTime(1970, 9, 3, 12, 0, 0) - && d.DateTime == new DateTime(1971, 9, 3, 12, 0, 10, 220) - && d.DateTime2 == new DateTime(1972, 9, 3, 12, 0, 10, 333) - && d.DateTime2_0 == new DateTime(1973, 9, 3, 12, 0, 10) - && d.DateTime2_1 == new DateTime(1974, 9, 3, 12, 0, 10, 500) - && d.DateTime2_2 == new DateTime(1975, 9, 3, 12, 0, 10, 660) - && d.DateTime2_3 == new DateTime(1976, 9, 3, 12, 0, 10, 777) - && d.DateTime2_4 == new DateTime(1977, 9, 3, 12, 0, 10, 888) - && d.DateTime2_5 == new DateTime(1978, 9, 3, 12, 0, 10, 999) - && d.DateTime2_6 == new DateTime(1979, 9, 3, 12, 0, 10, 111) - && d.DateTime2_7 == new DateTime(1980, 9, 3, 12, 0, 10, 222)); - - var results = async - ? await query.ToListAsync() - : query.ToList(); - - Assert.Single(results); + using var context = contextFactory.CreateContext(); + var query = context.Dates.Where( + d => d.DateTime == new DateTime(1971, 9, 3, 12, 0, 10, 220)); - AssertSql( - $@"SELECT `d`.`Id`, `d`.`DateTime`, `d`.`DateTime2`, `d`.`DateTime2_0`, `d`.`DateTime2_1`, `d`.`DateTime2_2`, `d`.`DateTime2_3`, `d`.`DateTime2_4`, `d`.`DateTime2_5`, `d`.`DateTime2_6`, `d`.`DateTime2_7`, `d`.`SmallDateTime` + var results = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Single(results); + + AssertSql( +""" +SELECT `d`.`Id`, `d`.`DateTime` FROM `Dates` AS `d` -WHERE ((((((((((`d`.`SmallDateTime` = '1970-09-03T12:00:00') AND (`d`.`DateTime` = '1971-09-03T12:00:10.220')) AND (`d`.`DateTime2` = '1972-09-03T12:00:10.3330000')) AND (`d`.`DateTime2_0` = '1973-09-03T12:00:10')) AND (`d`.`DateTime2_1` = '1974-09-03T12:00:10.5')) AND (`d`.`DateTime2_2` = '1975-09-03T12:00:10.66')) AND (`d`.`DateTime2_3` = '1976-09-03T12:00:10.777')) AND (`d`.`DateTime2_4` = '1977-09-03T12:00:10.8880')) AND (`d`.`DateTime2_5` = '1978-09-03T12:00:10.99900')) AND (`d`.`DateTime2_6` = '1979-09-03T12:00:10.111000')) AND (`d`.`DateTime2_7` = '1980-09-03T12:00:10.2220000')"); - } - } +WHERE `d`.`DateTime` = #1971-09-03 12:00:10# +"""); } [ConditionalTheory] @@ -156,91 +128,42 @@ WHERE ((((((((((`d`.`SmallDateTime` = '1970-09-03T12:00:00') AND (`d`.`DateTime` { var dateTimes = new[] { - new DateTime(1970, 9, 3, 12, 0, 0), - new DateTime(1971, 9, 3, 12, 0, 10, 220), - new DateTime(1972, 9, 3, 12, 0, 10, 333), - new DateTime(1973, 9, 3, 12, 0, 10), - new DateTime(1974, 9, 3, 12, 0, 10, 500), - new DateTime(1975, 9, 3, 12, 0, 10, 660), - new DateTime(1976, 9, 3, 12, 0, 10, 777), - new DateTime(1977, 9, 3, 12, 0, 10, 888), - new DateTime(1978, 9, 3, 12, 0, 10, 999), - new DateTime(1979, 9, 3, 12, 0, 10, 111), - new DateTime(1980, 9, 3, 12, 0, 10, 222) - }; - - using (CreateDateTimeStore()) - { - Fixture.TestSqlLoggerFactory.Clear(); - - using (var context = new DateTimeContext(_options)) - { - var query = context.Dates.Where( - d => dateTimes.Contains(d.SmallDateTime) - && dateTimes.Contains(d.DateTime) - && dateTimes.Contains(d.DateTime2) - && dateTimes.Contains(d.DateTime2_0) - && dateTimes.Contains(d.DateTime2_1) - && dateTimes.Contains(d.DateTime2_2) - && dateTimes.Contains(d.DateTime2_3) - && dateTimes.Contains(d.DateTime2_4) - && dateTimes.Contains(d.DateTime2_5) - && dateTimes.Contains(d.DateTime2_6) - && dateTimes.Contains(d.DateTime2_7)); - - var results = async - ? await query.ToListAsync() - : query.ToList(); - - Assert.Single(results); - - AssertSql( - $@"SELECT `d`.`Id`, `d`.`DateTime`, `d`.`DateTime2`, `d`.`DateTime2_0`, `d`.`DateTime2_1`, `d`.`DateTime2_2`, `d`.`DateTime2_3`, `d`.`DateTime2_4`, `d`.`DateTime2_5`, `d`.`DateTime2_6`, `d`.`DateTime2_7`, `d`.`SmallDateTime` + new DateTime(1970, 9, 3, 12, 0, 0), + new DateTime(1971, 9, 3, 12, 0, 10, 220), + new DateTime(1972, 9, 3, 12, 0, 10, 333), + new DateTime(1973, 9, 3, 12, 0, 10), + new DateTime(1974, 9, 3, 12, 0, 10, 500), + new DateTime(1975, 9, 3, 12, 0, 10, 660), + new DateTime(1976, 9, 3, 12, 0, 10, 777), + new DateTime(1977, 9, 3, 12, 0, 10, 888), + new DateTime(1978, 9, 3, 12, 0, 10, 999), + new DateTime(1979, 9, 3, 12, 0, 10, 111), + new DateTime(1980, 9, 3, 12, 0, 10, 222) + }; + + var contextFactory = await InitializeDateTimeContextAsync(); + + using var context = contextFactory.CreateContext(); + var query = context.Dates.Where( + d => dateTimes.Contains(d.DateTime)); + + var results = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Single(results); + + // TODO: The parameters values below are incorrect, since we currently don't take the element type mapping into account when + // generating the JSON representation (#30677) + AssertSql( +""" +SELECT `d`.`Id`, `d`.`DateTime` FROM `Dates` AS `d` -WHERE (((((((((`d`.`SmallDateTime` IN ('1970-09-03T12:00:00', '1971-09-03T12:00:10', '1972-09-03T12:00:10', '1973-09-03T12:00:10', '1974-09-03T12:00:10', '1975-09-03T12:00:10', '1976-09-03T12:00:10', '1977-09-03T12:00:10', '1978-09-03T12:00:10', '1979-09-03T12:00:10', '1980-09-03T12:00:10') AND `d`.`DateTime` IN ( #09/03/1970 12:00:00#, '1971-09-03T12:00:10.220', '1972-09-03T12:00:10.333', #09/03/1973 12:00:10#, '1974-09-03T12:00:10.500', '1975-09-03T12:00:10.660', '1976-09-03T12:00:10.777', '1977-09-03T12:00:10.888', '1978-09-03T12:00:10.999', '1979-09-03T12:00:10.111', '1980-09-03T12:00:10.222')) AND `d`.`DateTime2` IN ('1970-09-03T12:00:00.0000000', '1971-09-03T12:00:10.2200000', '1972-09-03T12:00:10.3330000', '1973-09-03T12:00:10.0000000', '1974-09-03T12:00:10.5000000', '1975-09-03T12:00:10.6600000', '1976-09-03T12:00:10.7770000', '1977-09-03T12:00:10.8880000', '1978-09-03T12:00:10.9990000', '1979-09-03T12:00:10.1110000', '1980-09-03T12:00:10.2220000')) AND `d`.`DateTime2_0` IN ('1970-09-03T12:00:00', '1971-09-03T12:00:10', '1972-09-03T12:00:10', '1973-09-03T12:00:10', '1974-09-03T12:00:10', '1975-09-03T12:00:10', '1976-09-03T12:00:10', '1977-09-03T12:00:10', '1978-09-03T12:00:10', '1979-09-03T12:00:10', '1980-09-03T12:00:10')) AND `d`.`DateTime2_1` IN ('1970-09-03T12:00:00.0', '1971-09-03T12:00:10.2', '1972-09-03T12:00:10.3', '1973-09-03T12:00:10.0', '1974-09-03T12:00:10.5', '1975-09-03T12:00:10.6', '1976-09-03T12:00:10.7', '1977-09-03T12:00:10.8', '1978-09-03T12:00:10.9', '1979-09-03T12:00:10.1', '1980-09-03T12:00:10.2')) AND `d`.`DateTime2_2` IN ('1970-09-03T12:00:00.00', '1971-09-03T12:00:10.22', '1972-09-03T12:00:10.33', '1973-09-03T12:00:10.00', '1974-09-03T12:00:10.50', '1975-09-03T12:00:10.66', '1976-09-03T12:00:10.77', '1977-09-03T12:00:10.88', '1978-09-03T12:00:10.99', '1979-09-03T12:00:10.11', '1980-09-03T12:00:10.22')) AND `d`.`DateTime2_3` IN ( #09/03/1970 12:00:00#, '1971-09-03T12:00:10.220', '1972-09-03T12:00:10.333', #09/03/1973 12:00:10#, '1974-09-03T12:00:10.500', '1975-09-03T12:00:10.660', '1976-09-03T12:00:10.777', '1977-09-03T12:00:10.888', '1978-09-03T12:00:10.999', '1979-09-03T12:00:10.111', '1980-09-03T12:00:10.222')) AND `d`.`DateTime2_4` IN ('1970-09-03T12:00:00.0000', '1971-09-03T12:00:10.2200', '1972-09-03T12:00:10.3330', '1973-09-03T12:00:10.0000', '1974-09-03T12:00:10.5000', '1975-09-03T12:00:10.6600', '1976-09-03T12:00:10.7770', '1977-09-03T12:00:10.8880', '1978-09-03T12:00:10.9990', '1979-09-03T12:00:10.1110', '1980-09-03T12:00:10.2220')) AND `d`.`DateTime2_5` IN ('1970-09-03T12:00:00.00000', '1971-09-03T12:00:10.22000', '1972-09-03T12:00:10.33300', '1973-09-03T12:00:10.00000', '1974-09-03T12:00:10.50000', '1975-09-03T12:00:10.66000', '1976-09-03T12:00:10.77700', '1977-09-03T12:00:10.88800', '1978-09-03T12:00:10.99900', '1979-09-03T12:00:10.11100', '1980-09-03T12:00:10.22200')) AND `d`.`DateTime2_6` IN ('1970-09-03T12:00:00.000000', '1971-09-03T12:00:10.220000', '1972-09-03T12:00:10.333000', '1973-09-03T12:00:10.000000', '1974-09-03T12:00:10.500000', '1975-09-03T12:00:10.660000', '1976-09-03T12:00:10.777000', '1977-09-03T12:00:10.888000', '1978-09-03T12:00:10.999000', '1979-09-03T12:00:10.111000', '1980-09-03T12:00:10.222000')) AND `d`.`DateTime2_7` IN ('1970-09-03T12:00:00.0000000', '1971-09-03T12:00:10.2200000', '1972-09-03T12:00:10.3330000', '1973-09-03T12:00:10.0000000', '1974-09-03T12:00:10.5000000', '1975-09-03T12:00:10.6600000', '1976-09-03T12:00:10.7770000', '1977-09-03T12:00:10.8880000', '1978-09-03T12:00:10.9990000', '1979-09-03T12:00:10.1110000', '1980-09-03T12:00:10.2220000')"); - } - } +WHERE `d`.`DateTime` IN (#1970-09-03 12:00:00#, #1971-09-03 12:00:10#, #1972-09-03 12:00:10#, #1973-09-03 12:00:10#, #1974-09-03 12:00:10#, #1975-09-03 12:00:10#, #1976-09-03 12:00:10#, #1977-09-03 12:00:10#, #1978-09-03 12:00:10#, #1979-09-03 12:00:10#, #1980-09-03 12:00:10#) +"""); } - private class DatesAndPrunes - { - public int Id { get; set; } - - [Column(TypeName = "smalldatetime")] - public DateTime SmallDateTime { get; set; } - - [Column(TypeName = "datetime")] - public DateTime DateTime { get; set; } - - [Column(TypeName = "datetime2")] - public DateTime DateTime2 { get; set; } - - [Column(TypeName = "datetime2(0)")] - public DateTime DateTime2_0 { get; set; } - - [Column(TypeName = "datetime2(1)")] - public DateTime DateTime2_1 { get; set; } - - [Column(TypeName = "datetime2(2)")] - public DateTime DateTime2_2 { get; set; } - - [Column(TypeName = "datetime2(3)")] - public DateTime DateTime2_3 { get; set; } - - [Column(TypeName = "datetime2(4)")] - public DateTime DateTime2_4 { get; set; } - - [Column(TypeName = "datetime2(5)")] - public DateTime DateTime2_5 { get; set; } - - [Column(TypeName = "datetime2(6)")] - public DateTime DateTime2_6 { get; set; } - - [Column(TypeName = "datetime2(7)")] - public DateTime DateTime2_7 { get; set; } - } - - private class DateTimeContext : DbContext + protected class DateTimeContext : DbContext { public DateTimeContext(DbContextOptions options) : base(options) @@ -248,96 +171,132 @@ WHERE (((((((((`d`.`SmallDateTime` IN ('1970-09-03T12:00:00', '1971-09-03T12:00: } public DbSet Dates { get; set; } + + public void Seed() + { + Add( + new DatesAndPrunes + { + DateTime = new DateTime(1971, 9, 3, 12, 0, 10, 220) + }); + SaveChanges(); + } + + public class DatesAndPrunes + { + public int Id { get; set; } + + [Column(TypeName = "datetime")] + public DateTime DateTime { get; set; } + } } - private JetTestStore CreateDateTimeStore() - => CreateTestStore( - () => new DateTimeContext(_options), - c => - { - c.Add( - new DatesAndPrunes - { - SmallDateTime = new DateTime(1970, 9, 3, 12, 0, 0), - DateTime = new DateTime(1971, 9, 3, 12, 0, 10, 220), - DateTime2 = new DateTime(1972, 9, 3, 12, 0, 10, 333), - DateTime2_0 = new DateTime(1973, 9, 3, 12, 0, 10), - DateTime2_1 = new DateTime(1974, 9, 3, 12, 0, 10, 500), - DateTime2_2 = new DateTime(1975, 9, 3, 12, 0, 10, 660), - DateTime2_3 = new DateTime(1976, 9, 3, 12, 0, 10, 777), - DateTime2_4 = new DateTime(1977, 9, 3, 12, 0, 10, 888), - DateTime2_5 = new DateTime(1978, 9, 3, 12, 0, 10, 999), - DateTime2_6 = new DateTime(1979, 9, 3, 12, 0, 10, 111), - DateTime2_7 = new DateTime(1980, 9, 3, 12, 0, 10, 222) - }); + protected Task> InitializeDateTimeContextAsync() + => InitializeAsync(seed: c => c.Seed()); - c.SaveChanges(); - }); + #endregion - #region Bug6901 + #region Issue6901 [ConditionalFact] - public void Left_outer_join_bug_6091() - { - using (var testStore = JetTestStore.CreateInitialized("QueryBugsTest")) - { - testStore.ExecuteNonQuery( - @" -CREATE TABLE `Customers`( - `CustomerID` `int` NOT NULL PRIMARY KEY, - `CustomerName` `varchar`(120) NULL, - `PostcodeID` `int` NULL); - -CREATE TABLE `Postcodes`( - `PostcodeID` `int` NOT NULL PRIMARY KEY, - `PostcodeValue` `varchar`(100) NOT NULL, - `TownName` `varchar`(255) NOT NULL); - -INSERT `Customers` (`CustomerID`, `CustomerName`, `PostcodeID`) VALUES (1, 'Sam Tippet', 5); -INSERT `Customers` (`CustomerID`, `CustomerName`, `PostcodeID`) VALUES (2, 'William Greig', 2); -INSERT `Customers` (`CustomerID`, `CustomerName`, `PostcodeID`) VALUES (3, 'Steve Jones', 3); -INSERT `Customers` (`CustomerID`, `CustomerName`, `PostcodeID`) VALUES (4, 'Jim Warren', NULL); -INSERT `Customers` (`CustomerID`, `CustomerName`, `PostcodeID`) VALUES (5, 'Andrew Smith', 5); - -INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (2, '1000', 'Town 1'); -INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (3, '2000', 'Town 2'); -INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (4, '3000', 'Town 3'); -INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000', 'Town 4'); -"); - - using (var context = new Bug6091Context(Fixture.CreateOptions(testStore))) - { - var customers - = from customer in context.Customers - join postcode in context.Postcodes - on customer.PostcodeID equals postcode.PostcodeID into custPCTmp - from custPC in custPCTmp.DefaultIfEmpty() - select new - { - customer.CustomerID, - customer.CustomerName, - TownName = custPC == null ? string.Empty : custPC.TownName, - PostcodeValue = custPC == null ? string.Empty : custPC.PostcodeValue - }; - - var results = customers.ToList(); - - Assert.Equal(5, results.Count); - Assert.True(results[3].CustomerName != results[4].CustomerName); - } - } + public async Task Left_outer_join_Issue_6091() + { + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + var customers + = from customer in context.Customers + join postcode in context.Postcodes + on customer.PostcodeID equals postcode.PostcodeID into custPCTmp + from custPC in custPCTmp.DefaultIfEmpty() + select new + { + customer.CustomerID, + customer.CustomerName, + TownName = custPC == null ? string.Empty : custPC.TownName, + PostcodeValue = custPC == null ? string.Empty : custPC.PostcodeValue + }; + + var results = customers.ToList(); + + Assert.Equal(5, results.Count); + Assert.True(results[3].CustomerName != results[4].CustomerName); } - private class Bug6091Context : DbContext + protected class Issue6091Context : DbContext { - public Bug6091Context(DbContextOptions options) + public Issue6091Context(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().ToTable("Customers"); + modelBuilder.Entity( + c => + { + c.Property(c => c.CustomerID).ValueGeneratedNever(); + c.Property(c => c.CustomerName).HasMaxLength(120).IsUnicode(false); + c.HasData( + new Customer + { + CustomerID = 1, + CustomerName = "Sam Tippet", + PostcodeID = 5 + }, + new Customer + { + CustomerID = 2, + CustomerName = "William Greig", + PostcodeID = 2 + }, + new Customer + { + CustomerID = 3, + CustomerName = "Steve Jones", + PostcodeID = 3 + }, + new Customer { CustomerID = 4, CustomerName = "Jim Warren" }, + new Customer + { + CustomerID = 5, + CustomerName = "Andrew Smith", + PostcodeID = 5 + }); + }); + + modelBuilder.Entity( + p => + { + p.Property(c => c.PostcodeID).ValueGeneratedNever(); + p.Property(c => c.PostcodeValue).HasMaxLength(100).IsUnicode(false); + p.Property(c => c.TownName).HasMaxLength(255).IsUnicode(false); + p.HasData( + new Postcode + { + PostcodeID = 2, + PostcodeValue = "1000", + TownName = "Town 1" + }, + new Postcode + { + PostcodeID = 3, + PostcodeValue = "2000", + TownName = "Town 2" + }, + new Postcode + { + PostcodeID = 4, + PostcodeValue = "3000", + TownName = "Town 3" + }, + new Postcode + { + PostcodeID = 5, + PostcodeValue = "4000", + TownName = "Town 4" + }); + }); } public DbSet Customers { get; set; } @@ -361,31 +320,26 @@ INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000' #endregion - #region Bug5481 + #region Issue5481 [ConditionalFact] - public async Task Multiple_optional_navs_should_not_deadlock_bug_5481() + public async Task Multiple_optional_navs_should_not_deadlock_Issue_5481() { - using (var testStore = JetTestStore.CreateInitialized("QueryBugsTest")) - { - using (var context = new DeadlockContext(Fixture.CreateOptions(testStore))) - { - context.Database.EnsureCreatedResiliently(); - context.EnsureSeeded(); + var contextFactory = await InitializeAsync(); - var count - = await context.Persons - .Where( - p => p.AddressOne != null && p.AddressOne.Street.Contains("Low Street") - || p.AddressTwo != null && p.AddressTwo.Street.Contains("Low Street")) - .CountAsync(); + using var context = contextFactory.CreateContext(); - Assert.Equal(0, count); - } - } + var count + = await context.Persons + .Where( + p => p.AddressOne != null && p.AddressOne.Street.Contains("Low Street") + || p.AddressTwo != null && p.AddressTwo.Street.Contains("Low Street")) + .CountAsync(); + + Assert.Equal(0, count); } - private class DeadlockContext : DbContext + protected class DeadlockContext : DbContext { public DeadlockContext(DbContextOptions options) : base(options) @@ -461,25 +415,25 @@ INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000' #endregion - [ConditionalFact(Skip = "Issue#15751")] - public void Query_when_null_key_in_database_should_throw() + #region Issue2951 + + [ConditionalFact] + public async Task Query_when_null_key_in_database_should_throw() { - using (var testStore = JetTestStore.CreateInitialized("QueryBugsTest")) - { - testStore.ExecuteNonQuery( - @"CREATE TABLE ZeroKey (Id int); - INSERT ZeroKey VALUES (NULL)"); + var contextFactory = await InitializeAsync(onConfiguring: o => o.EnableDetailedErrors()); - using (var context = new NullKeyContext(Fixture.CreateOptions(testStore))) - { - // Assert.Equal( - // CoreStrings.ErrorMaterializingPropertyNullReference("ZeroKey", "Id", typeof(int)), - // Assert.Throws(() => context.ZeroKeys.ToList()).Message); - } - } + using var context = contextFactory.CreateContext(); + await context.Database.ExecuteSqlRawAsync( + @" +CREATE TABLE ZeroKey (Id int); +INSERT ZeroKey VALUES (NULL)"); + + Assert.Equal( + RelationalStrings.ErrorMaterializingPropertyNullReference("ZeroKey", "Id", typeof(int)), + Assert.Throws(() => context.ZeroKeys.ToList()).Message); } - private class NullKeyContext : DbContext + protected class NullKeyContext : DbContext { public NullKeyContext(DbContextOptions options) : base(options) @@ -487,9 +441,8 @@ INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000' } protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().ToTable("ZeroKey"); - } + => modelBuilder.Entity().ToTable("ZeroKey", t => t.ExcludeFromMigrations()) + .Property(z => z.Id).ValueGeneratedNever(); public DbSet ZeroKeys { get; set; } @@ -499,58 +452,42 @@ INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000' } } - #region Bug603 + #endregion + + #region Issue603 [ConditionalFact] - public async Task First_FirstOrDefault_ix_async_bug_603() + public async Task First_FirstOrDefault_ix_async_Issue_603() { - using (CreateDatabase603()) - { - using (var context = new MyContext603(_options)) - { - context.Products.Add( - new Product { Name = "Product 1" }); - context.SaveChanges(); - } + var contextFactory = await InitializeAsync(); - using (var ctx = new MyContext603(_options)) - { - var product = await ctx.Products.OrderBy(p => p.Id).FirstAsync(); + using (var context = contextFactory.CreateContext()) + { + var product = await context.Products.OrderBy(p => p.Id).FirstAsync(); - ctx.Products.Remove(product); + context.Products.Remove(product); - await ctx.SaveChangesAsync(); - } + await context.SaveChangesAsync(); } - using (CreateDatabase603()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext603(_options)) - { - context.Products.Add( - new Product { Name = "Product 1" }); - context.SaveChanges(); - } + context.Products.Add( + new MyContext603.Product { Name = "Product 1" }); + context.SaveChanges(); + } - using (var ctx = new MyContext603(_options)) - { - var product = await ctx.Products.OrderBy(p => p.Id).FirstOrDefaultAsync(); + using (var context = contextFactory.CreateContext()) + { + var product = await context.Products.OrderBy(p => p.Id).FirstOrDefaultAsync(); - ctx.Products.Remove(product); + context.Products.Remove(product); - await ctx.SaveChangesAsync(); - } + await context.SaveChangesAsync(); } } - private class Product - { - public int Id { get; set; } - - public string Name { get; set; } - } - - private class MyContext603 : DbContext + protected class MyContext603 : DbContext { public MyContext603(DbContextOptions options) : base(options) @@ -560,118 +497,65 @@ INSERT `Postcodes` (`PostcodeID`, `PostcodeValue`, `TownName`) VALUES (5, '4000' public DbSet Products { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().ToTable("Product") + .HasData(new Product { Id = 1, Name = "Product 1" }); + + public class Product { - modelBuilder.Entity().ToTable("Product"); - } - } + public int Id { get; set; } - private JetTestStore CreateDatabase603() - { - return CreateTestStore(() => new MyContext603(_options), null); + public string Name { get; set; } + } } #endregion - #region Bugs925_926 + #region Issues925_926 [ConditionalFact] - public void Include_on_entity_with_composite_key_One_To_Many_bugs_925_926() + public async Task Include_on_entity_with_composite_key_One_To_Many_Issues_925_926() { - using (CreateDatabase925()) - { - using (var ctx = new MyContext925(_options)) - { - var query = ctx.Customers.Include(c => c.Orders).OrderBy(c => c.FirstName).ThenBy(c => c.LastName); - var result = query.ToList(); + var contextFactory = await InitializeAsync(); - Assert.Equal(2, result.Count); - Assert.Equal(2, result[0].Orders.Count); - Assert.Equal(3, result[1].Orders.Count); + using var ctx = contextFactory.CreateContext(); + var query = ctx.Customers.Include(c => c.Orders).OrderBy(c => c.FirstName).ThenBy(c => c.LastName); + var result = query.ToList(); - AssertSql( - $@"SELECT `c`.`FirstName`, `c`.`LastName`, `o`.`Id`, `o`.`CustomerFirstName`, `o`.`CustomerLastName`, `o`.`Name` + Assert.Equal(2, result.Count); + Assert.Equal(2, result[0].Orders.Count); + Assert.Equal(3, result[1].Orders.Count); + + AssertSql( +""" +SELECT `c`.`FirstName`, `c`.`LastName`, `o`.`Id`, `o`.`CustomerFirstName`, `o`.`CustomerLastName`, `o`.`Name` FROM `Customer` AS `c` -LEFT JOIN `Order` AS `o` ON (`c`.`FirstName` = `o`.`CustomerFirstName`) AND (`c`.`LastName` = `o`.`CustomerLastName`) -ORDER BY `c`.`FirstName`, `c`.`LastName`, `o`.`Id`"); - } - } +LEFT JOIN `Order` AS `o` ON `c`.`FirstName` = `o`.`CustomerFirstName` AND `c`.`LastName` = `o`.`CustomerLastName` +ORDER BY `c`.`FirstName`, `c`.`LastName` +"""); } [ConditionalFact] - public void Include_on_entity_with_composite_key_Many_To_One_bugs_925_926() - { - using (CreateDatabase925()) - { - using (var ctx = new MyContext925(_options)) - { - var query = ctx.Orders.Include(o => o.Customer); - var result = query.ToList(); - - Assert.Equal(5, result.Count); - Assert.NotNull(result[0].Customer); - Assert.NotNull(result[1].Customer); - Assert.NotNull(result[2].Customer); - Assert.NotNull(result[3].Customer); - Assert.NotNull(result[4].Customer); - - AssertSql( - $@"SELECT `o`.`Id`, `o`.`CustomerFirstName`, `o`.`CustomerLastName`, `o`.`Name`, `c`.`FirstName`, `c`.`LastName` -FROM `Order` AS `o` -LEFT JOIN `Customer` AS `c` ON (`o`.`CustomerFirstName` = `c`.`FirstName`) AND (`o`.`CustomerLastName` = `c`.`LastName`)"); - } - } - } - - private JetTestStore CreateDatabase925() + public async Task Include_on_entity_with_composite_key_Many_To_One_Issues_925_926() { - return CreateTestStore( - () => new MyContext925(_options), - context => - { - var order11 = new Order { Name = "Order11" }; - var order12 = new Order { Name = "Order12" }; - var order21 = new Order { Name = "Order21" }; - var order22 = new Order { Name = "Order22" }; - var order23 = new Order { Name = "Order23" }; - - var customer1 = new Customer - { - FirstName = "Customer", - LastName = "One", - Orders = new List { order11, order12 } - }; - var customer2 = new Customer - { - FirstName = "Customer", - LastName = "Two", - Orders = new List - { - order21, - order22, - order23 - } - }; + var contextFactory = await InitializeAsync(); - context.Customers.AddRange(customer1, customer2); - context.Orders.AddRange(order11, order12, order21, order22, order23); - context.SaveChanges(); - - ClearLog(); - }); - } + using var ctx = contextFactory.CreateContext(); + var query = ctx.Orders.Include(o => o.Customer); + var result = query.ToList(); - private class Customer - { - public string FirstName { get; set; } - public string LastName { get; set; } - public List Orders { get; set; } - } + Assert.Equal(5, result.Count); + Assert.NotNull(result[0].Customer); + Assert.NotNull(result[1].Customer); + Assert.NotNull(result[2].Customer); + Assert.NotNull(result[3].Customer); + Assert.NotNull(result[4].Customer); - private class Order - { - public int Id { get; set; } - public string Name { get; set; } - public Customer Customer { get; set; } + AssertSql( +""" +SELECT `o`.`Id`, `o`.`CustomerFirstName`, `o`.`CustomerLastName`, `o`.`Name`, `c`.`FirstName`, `c`.`LastName` +FROM `Order` AS `o` +LEFT JOIN `Customer` AS `c` ON `o`.`CustomerFirstName` = `c`.`FirstName` AND `o`.`CustomerLastName` = `c`.`LastName` +"""); } private class MyContext925 : DbContext @@ -693,317 +577,226 @@ LEFT JOIN `Customer` AS `c` ON (`o`.`CustomerFirstName` = `c`.`FirstName`) AND ( m.HasKey( c => new { c.FirstName, c.LastName }); m.HasMany(c => c.Orders).WithOne(o => o.Customer); + m.HasData( + new Customer { FirstName = "Customer", LastName = "One" }, + new Customer { FirstName = "Customer", LastName = "Two" }); }); - modelBuilder.Entity().ToTable("Order"); + modelBuilder.Entity().ToTable("Order") + .HasData( + new + { + Id = 1, + Name = "Order11", + CustomerFirstName = "Customer", + CustomerLastName = "One" + }, + new + { + Id = 2, + Name = "Order12", + CustomerFirstName = "Customer", + CustomerLastName = "One" + }, + new + { + Id = 3, + Name = "Order21", + CustomerFirstName = "Customer", + CustomerLastName = "Two" + }, + new + { + Id = 4, + Name = "Order22", + CustomerFirstName = "Customer", + CustomerLastName = "Two" + }, + new + { + Id = 5, + Name = "Order23", + CustomerFirstName = "Customer", + CustomerLastName = "Two" + }); + } + + public class Customer + { + public string FirstName { get; set; } + public string LastName { get; set; } + public List Orders { get; set; } + } + + public class Order + { + public int Id { get; set; } + public string Name { get; set; } + public Customer Customer { get; set; } } } #endregion - #region Bug7293 + #region Issue963 - [ConditionalFact(Skip = "Issue #17068")] - public void GroupJoin_expansion_when_optional_nav_in_projection() + [ConditionalFact] + public async Task Include_on_optional_navigation() { - using (CreateDatabase7293()) + var contextFactory = await InitializeAsync(); + + using (var ctx = contextFactory.CreateContext()) { - using (var context = new Context7293(_options)) - { - //TestSqlLoggerFactory.CaptureOutput(_testOutputHelper); + var targaryens = ctx.Targaryens.Include(t => t.Dragons).ToList(); - var query = from p in context.Projects - select new ProjectView - { - Permissions - = from u in p.ProjectUsers - select new PermissionView { UserName = u.User.Name } - }; + Assert.All(targaryens, t => Assert.NotNull(t.Dragons)); + } - var target = context.ProjectUsers.OrderBy(u => u.Id).First(); + using (var ctx = contextFactory.CreateContext()) + { + var dragons = ctx.Dragons.Include(d => d.Mother).ToList(); - query.SingleOrDefault(item => item.Id == target.ProjectId); - } + dragons = dragons.OrderBy(d => d.Id).ToList(); + + Assert.Collection( + dragons, + t => Assert.NotNull(t.Mother), + t => Assert.NotNull(t.Mother), + t => Assert.NotNull(t.Mother), + t => Assert.Null(t.Mother)); } - } - private interface IHasKey - { - Guid Id { get; set; } - } + using (var ctx = contextFactory.CreateContext()) + { + var targaryens = ctx.Targaryens.Include(t => t.Details).ToList(); - private class Project : IHasKey - { - public Guid Id { get; set; } - public string Name { get; set; } + targaryens = targaryens.OrderBy(d => d.Id).ToList(); - // ReSharper disable once CollectionNeverUpdated.Local - public ISet ProjectUsers { get; set; } - } + Assert.Collection( + targaryens, + t => Assert.Null(t.Details), + t => Assert.NotNull(t.Details)); + } - private class ProjectUser : IHasKey - { - public Guid Id { get; set; } - public Guid ProjectId { get; set; } - public Project Project { get; set; } - public Guid UserId { get; set; } - public User User { get; set; } - } + using (var ctx = contextFactory.CreateContext()) + { + var details = ctx.Details.Include(d => d.Targaryen).ToList(); - private class User : IHasKey - { - public Guid Id { get; set; } - public string Name { get; set; } - } + Assert.All(details, t => Assert.NotNull(t.Targaryen)); + } - private class ProjectView : IHasKey - { - public Guid Id { get; set; } - public string Name { get; set; } - public IEnumerable Permissions { get; set; } - } + using (var ctx = contextFactory.CreateContext()) + { + var dragons = (from t in ctx.Targaryens + join d in ctx.Dragons on t.Id equals d.MotherId + select d).ToList(); - private class PermissionView : IHasKey - { - public Guid Id { get; set; } - public Guid UserId { get; set; } - public string UserName { get; set; } + Assert.Equal(3, dragons.Count()); + } } - private class Context7293 : DbContext + protected class MyContext963 : DbContext { - public Context7293(DbContextOptions options) + public MyContext963(DbContextOptions options) : base(options) { } - public DbSet Projects { get; set; } - public DbSet ProjectUsers { get; set; } - public DbSet Users { get; set; } - } + public DbSet Targaryens { get; set; } - private JetTestStore CreateDatabase7293() - { - return CreateTestStore( - () => new Context7293(_options), - context => - { - var projects = new[] - { - new Project { Name = "Projects 1" }, new Project { Name = "Projects 2" }, new Project { Name = "Projects 3" } - }; + public DbSet Details { get; set; } - context.Projects.AddRange(projects); + public DbSet Dragons { get; set; } - var users = new[] { new User { Name = "Users 1" }, new User { Name = "Users 2" }, new User { Name = "Users 3" } }; + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + m => + { + m.ToTable("Targaryen"); + m.HasKey(t => t.Id); + m.HasMany(t => t.Dragons).WithOne(d => d.Mother).HasForeignKey(d => d.MotherId); + m.HasOne(t => t.Details).WithOne(d => d.Targaryen).HasForeignKey(d => d.TargaryenId); + m.HasData( + new Targaryen { Id = 1, Name = "Aerys II" }, + new Targaryen { Id = 2, Name = "Daenerys" }); + }); - context.Users.AddRange(users); + modelBuilder.Entity().HasData( + new TargaryenDetails + { + Id = 2, + TargaryenId = 2, + FullName = @"Daenerys Stormborn of the House Targaryen, the First of Her Name, the Unburnt, Queen of Meereen, +Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons" + }); - var permissions = (from project in projects - from user in users - select new ProjectUser - { - ProjectId = project.Id, - Project = project, - UserId = user.Id, - User = user - }) - .ToList(); + modelBuilder.Entity().ToTable("Dragon") + .HasData( + new Dragon + { + Id = 1, + Name = "Drogon", + MotherId = 2 + }, + new Dragon + { + Id = 2, + Name = "Rhaegal", + MotherId = 2 + }, + new Dragon + { + Id = 3, + Name = "Viserion", + MotherId = 2 + }, + new Dragon { Id = 4, Name = "Balerion" }); + } - context.ProjectUsers.AddRange(permissions); - context.SaveChanges(); - }); - } + public class Targaryen + { + public int Id { get; set; } + public string Name { get; set; } + public TargaryenDetails Details { get; set; } - #endregion + public List Dragons { get; set; } + } - #region Bug963 - - [ConditionalFact] - public void Include_on_optional_navigation_One_To_Many_963() - { - using (CreateDatabase963()) - { - using (var ctx = new MyContext963(_options)) - { - ctx.Targaryens.Include(t => t.Dragons).ToList(); - } - } - } - - [ConditionalFact] - public void Include_on_optional_navigation_Many_To_One_963() - { - using (CreateDatabase963()) - { - using (var ctx = new MyContext963(_options)) - { - ctx.Dragons.Include(d => d.Mother).ToList(); - } - } - } - - [ConditionalFact] - public void Include_on_optional_navigation_One_To_One_principal_963() - { - using (CreateDatabase963()) - { - using (var ctx = new MyContext963(_options)) - { - ctx.Targaryens.Include(t => t.Details).ToList(); - } - } - } - - [ConditionalFact] - public void Include_on_optional_navigation_One_To_One_dependent_963() - { - using (CreateDatabase963()) - { - using (var ctx = new MyContext963(_options)) - { - ctx.Details.Include(d => d.Targaryen).ToList(); - } - } - } - - [ConditionalFact] - public void Join_on_optional_navigation_One_To_Many_963() - { - using (CreateDatabase963()) - { - using (var ctx = new MyContext963(_options)) - { - (from t in ctx.Targaryens - join d in ctx.Dragons on t.Id equals d.MotherId - select d).ToList(); - } - } - } - - private JetTestStore CreateDatabase963() - { - return CreateTestStore( - () => new MyContext963(_options), - context => - { - var drogon = new Dragon { Name = "Drogon" }; - var rhaegal = new Dragon { Name = "Rhaegal" }; - var viserion = new Dragon { Name = "Viserion" }; - var balerion = new Dragon { Name = "Balerion" }; - - var aerys = new Targaryen { Name = "Aerys II" }; - var details = new Details - { - FullName = @"Daenerys Stormborn of the House Targaryen, the First of Her Name, the Unburnt, Queen of Meereen, -Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons" - }; - - var daenerys = new Targaryen - { - Name = "Daenerys", - Details = details, - Dragons = new List - { - drogon, - rhaegal, - viserion - } - }; - context.Targaryens.AddRange(daenerys, aerys); - context.Dragons.AddRange(drogon, rhaegal, viserion, balerion); - context.Details.Add(details); - - context.SaveChanges(); - }); - } - - private class Targaryen - { - public int Id { get; set; } - public string Name { get; set; } - public Details Details { get; set; } - - public List Dragons { get; set; } - } - - private class Dragon - { - public int Id { get; set; } - public string Name { get; set; } - public int? MotherId { get; set; } - public Targaryen Mother { get; set; } - } - - private class Details - { - public int Id { get; set; } - public int? TargaryenId { get; set; } - public Targaryen Targaryen { get; set; } - public string FullName { get; set; } - } - - // TODO: replace with GearsOfWar context when it's refactored properly - private class MyContext963 : DbContext - { - public MyContext963(DbContextOptions options) - : base(options) + public class Dragon { + public int Id { get; set; } + public string Name { get; set; } + public int? MotherId { get; set; } + public Targaryen Mother { get; set; } } - public DbSet Targaryens { get; set; } - - // ReSharper disable once MemberHidesStaticFromOuterClass - public DbSet
Details { get; set; } - - public DbSet Dragons { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) + public class TargaryenDetails { - modelBuilder.Entity( - m => - { - m.ToTable("Targaryen"); - m.HasKey(t => t.Id); - m.HasMany(t => t.Dragons).WithOne(d => d.Mother).HasForeignKey(d => d.MotherId); - m.HasOne(t => t.Details).WithOne(d => d.Targaryen).HasForeignKey
(d => d.TargaryenId); - }); - - modelBuilder.Entity().ToTable("Dragon"); + public int Id { get; set; } + public int? TargaryenId { get; set; } + public Targaryen Targaryen { get; set; } + public string FullName { get; set; } } } #endregion - #region Bug1742 + #region Issue1742 [ConditionalFact] public void Compiler_generated_local_closure_produces_valid_parameter_name_1742() - { - Execute1742( - new CustomerDetails_1742 { FirstName = "Foo", LastName = "Bar" }); - } + => Execute1742(new CustomerDetails_1742 { FirstName = "Foo", LastName = "Bar" }); private void Execute1742(CustomerDetails_1742 details) { - using (CreateDatabase925()) - { - using (var ctx = new MyContext925(_options)) - { - var firstName = details.FirstName; - ctx.Customers.Where(c => c.FirstName == firstName && c.LastName == details.LastName).ToList(); - - // issue #16057 - // AssertSql( - // $@"{AssertSqlHelper.Declaration("@__firstName_0='Foo' (Size = 450)")} + var contextFactory = Initialize(); - //@__8__locals1_details_LastName_1='Bar' (Size = 450) + using var ctx = contextFactory.CreateContext(); + var firstName = details.FirstName; + ctx.Customers.Where(c => c.FirstName == firstName && c.LastName == details.LastName).ToList(); - //SELECT `c`.`FirstName`, `c`.`LastName` - //FROM `Customer` AS `c` - //WHERE ((`c`.`FirstName` = @__firstName_0) AND @__firstName_0 IS NOT NULL) AND ((`c`.`LastName` = @__8__locals1_details_LastName_1) AND @__8__locals1_details_LastName_1 IS NOT NULL)"); - } - } + // No AssertSQL since compiler generated variable names are different between local and CI } private class CustomerDetails_1742 @@ -1014,51 +807,49 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra #endregion - #region Bug3758 + #region Issue3758 [ConditionalFact] - public void Customer_collections_materialize_properly_3758() + public async Task Customer_collections_materialize_properly_3758() { - using (CreateDatabase3758()) - { - using (var ctx = new MyContext3758(_options)) - { - var query1 = ctx.Customers.Select(c => c.Orders1); - var result1 = query1.ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - Assert.Equal(2, result1.Count); - Assert.IsType>(result1[0]); - Assert.Equal(2, result1[0].Count); - Assert.Equal(2, result1[1].Count); + using var ctx = contextFactory.CreateContext(); - var query2 = ctx.Customers.Select(c => c.Orders2); - var result2 = query2.ToList(); + var query1 = ctx.Customers.Select(c => c.Orders1); + var result1 = query1.ToList(); - Assert.Equal(2, result2.Count); - Assert.IsType>(result2[0]); - Assert.Equal(2, result2[0].Count); - Assert.Equal(2, result2[1].Count); + Assert.Equal(2, result1.Count); + Assert.IsType>(result1[0]); + Assert.Equal(2, result1[0].Count); + Assert.Equal(2, result1[1].Count); - var query3 = ctx.Customers.Select(c => c.Orders3); - var result3 = query3.ToList(); + var query2 = ctx.Customers.Select(c => c.Orders2); + var result2 = query2.ToList(); - Assert.Equal(2, result3.Count); - Assert.IsType(result3[0]); - Assert.Equal(2, result3[0].Count); - Assert.Equal(2, result3[1].Count); + Assert.Equal(2, result2.Count); + Assert.IsType>(result2[0]); + Assert.Equal(2, result2[0].Count); + Assert.Equal(2, result2[1].Count); - var query4 = ctx.Customers.Select(c => c.Orders4); + var query3 = ctx.Customers.Select(c => c.Orders3); + var result3 = query3.ToList(); - Assert.Equal( - CoreStrings.NavigationCannotCreateType( - "Orders4", typeof(Customer3758).Name, - typeof(MyInvalidCollection3758).ShortDisplayName()), - Assert.Throws(() => query4.ToList()).Message); - } - } + Assert.Equal(2, result3.Count); + Assert.IsType(result3[0]); + Assert.Equal(2, result3[0].Count); + Assert.Equal(2, result3[1].Count); + + var query4 = ctx.Customers.Select(c => c.Orders4); + + Assert.Equal( + CoreStrings.NavigationCannotCreateType( + "Orders4", typeof(MyContext3758.Customer3758).Name, + typeof(MyContext3758.MyInvalidCollection3758).ShortDisplayName()), + Assert.Throws(() => query4.ToList()).Message); } - private class MyContext3758 : DbContext + protected class MyContext3758 : DbContext { public MyContext3758(DbContextOptions options) : base(options) @@ -1083,164 +874,158 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra modelBuilder.Entity().ToTable("Order3758"); } - } - private class Customer3758 - { - public int Id { get; set; } - public string Name { get; set; } + public void Seed() + { + var o111 = new Order3758 { Name = "O111" }; + var o112 = new Order3758 { Name = "O112" }; + var o121 = new Order3758 { Name = "O121" }; + var o122 = new Order3758 { Name = "O122" }; + var o131 = new Order3758 { Name = "O131" }; + var o132 = new Order3758 { Name = "O132" }; + var o141 = new Order3758 { Name = "O141" }; - public ICollection Orders1 { get; set; } - public MyGenericCollection3758 Orders2 { get; set; } - public MyNonGenericCollection3758 Orders3 { get; set; } - public MyInvalidCollection3758 Orders4 { get; set; } - } + var o211 = new Order3758 { Name = "O211" }; + var o212 = new Order3758 { Name = "O212" }; + var o221 = new Order3758 { Name = "O221" }; + var o222 = new Order3758 { Name = "O222" }; + var o231 = new Order3758 { Name = "O231" }; + var o232 = new Order3758 { Name = "O232" }; + var o241 = new Order3758 { Name = "O241" }; - private class Order3758 - { - public int Id { get; set; } - public string Name { get; set; } - } + var c1 = new Customer3758 + { + Name = "C1", + Orders1 = new List { o111, o112 }, + Orders2 = new MyGenericCollection3758(), + Orders3 = new MyNonGenericCollection3758(), + Orders4 = new MyInvalidCollection3758(42) + }; - private class MyGenericCollection3758 : List - { - } + c1.Orders2.AddRange(new[] { o121, o122 }); + c1.Orders3.AddRange(new[] { o131, o132 }); + c1.Orders4.Add(o141); - private class MyNonGenericCollection3758 : List - { - } + var c2 = new Customer3758 + { + Name = "C2", + Orders1 = new List { o211, o212 }, + Orders2 = new MyGenericCollection3758(), + Orders3 = new MyNonGenericCollection3758(), + Orders4 = new MyInvalidCollection3758(42) + }; - private class MyInvalidCollection3758 : List - { - public MyInvalidCollection3758(int argument) - { - var _ = argument; + c2.Orders2.AddRange(new[] { o221, o222 }); + c2.Orders3.AddRange(new[] { o231, o232 }); + c2.Orders4.Add(o241); + + Customers.AddRange(c1, c2); + Orders.AddRange( + o111, o112, o121, o122, + o131, o132, o141, o211, + o212, o221, o222, o231, + o232, o241); + + SaveChanges(); } - } - private JetTestStore CreateDatabase3758() - { - return CreateTestStore( - () => new MyContext3758(_options), - context => - { - var o111 = new Order3758 { Name = "O111" }; - var o112 = new Order3758 { Name = "O112" }; - var o121 = new Order3758 { Name = "O121" }; - var o122 = new Order3758 { Name = "O122" }; - var o131 = new Order3758 { Name = "O131" }; - var o132 = new Order3758 { Name = "O132" }; - var o141 = new Order3758 { Name = "O141" }; - - var o211 = new Order3758 { Name = "O211" }; - var o212 = new Order3758 { Name = "O212" }; - var o221 = new Order3758 { Name = "O221" }; - var o222 = new Order3758 { Name = "O222" }; - var o231 = new Order3758 { Name = "O231" }; - var o232 = new Order3758 { Name = "O232" }; - var o241 = new Order3758 { Name = "O241" }; - - var c1 = new Customer3758 - { - Name = "C1", - Orders1 = new List { o111, o112 }, - Orders2 = new MyGenericCollection3758(), - Orders3 = new MyNonGenericCollection3758(), - Orders4 = new MyInvalidCollection3758(42) - }; + public class Customer3758 + { + public int Id { get; set; } + public string Name { get; set; } - c1.Orders2.AddRange(new[] { o121, o122 }); - c1.Orders3.AddRange(new[] { o131, o132 }); - c1.Orders4.Add(o141); + public ICollection Orders1 { get; set; } + public MyGenericCollection3758 Orders2 { get; set; } + public MyNonGenericCollection3758 Orders3 { get; set; } + public MyInvalidCollection3758 Orders4 { get; set; } + } - var c2 = new Customer3758 - { - Name = "C2", - Orders1 = new List { o211, o212 }, - Orders2 = new MyGenericCollection3758(), - Orders3 = new MyNonGenericCollection3758(), - Orders4 = new MyInvalidCollection3758(42) - }; + public class Order3758 + { + public int Id { get; set; } + public string Name { get; set; } + } - c2.Orders2.AddRange(new[] { o221, o222 }); - c2.Orders3.AddRange(new[] { o231, o232 }); - c2.Orders4.Add(o241); + public class MyGenericCollection3758 : List + { + } - context.Customers.AddRange(c1, c2); - context.Orders.AddRange( - o111, o112, o121, o122, - o131, o132, o141, o211, - o212, o221, o222, o231, - o232, o241); + public class MyNonGenericCollection3758 : List + { + } - context.SaveChanges(); - }); + public class MyInvalidCollection3758 : List + { + public MyInvalidCollection3758(int argument) + { + var _ = argument; + } + } } #endregion - #region Bug3409 + #region Issue3409 [ConditionalFact] - public void ThenInclude_with_interface_navigations_3409() + public async Task ThenInclude_with_interface_navigations_3409() { - using (CreateDatabase3409()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext3409(_options)) - { - var results = context.Parents - .Include(p => p.ChildCollection) - .ThenInclude(c => c.SelfReferenceCollection) - .ToList(); - - Assert.Single(results); - Assert.Equal(1, results[0].ChildCollection.Count); - Assert.Equal(2, results[0].ChildCollection.Single().SelfReferenceCollection.Count); - } + var results = context.Parents + .Include(p => p.ChildCollection) + .ThenInclude(c => c.SelfReferenceCollection) + .ToList(); - using (var context = new MyContext3409(_options)) - { - var results = context.Children - .Select( - c => new { c.SelfReferenceBackNavigation, c.SelfReferenceBackNavigation.ParentBackNavigation }) - .ToList(); + Assert.Single(results); + Assert.Equal(1, results[0].ChildCollection.Count); + Assert.Equal(2, results[0].ChildCollection.Single().SelfReferenceCollection.Count); + } - Assert.Equal(3, results.Count); - Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); - Assert.Equal(2, results.Count(c => c.ParentBackNavigation != null)); - } + using (var context = contextFactory.CreateContext()) + { + var results = context.Children + .Select( + c => new { c.SelfReferenceBackNavigation, c.SelfReferenceBackNavigation.ParentBackNavigation }) + .ToList(); - using (var context = new MyContext3409(_options)) - { - var results = context.Children - .Select( - c => new - { - SelfReferenceBackNavigation - = EF.Property(c, "SelfReferenceBackNavigation"), - ParentBackNavigationB - = EF.Property( - EF.Property(c, "SelfReferenceBackNavigation"), - "ParentBackNavigation") - }) - .ToList(); + Assert.Equal(3, results.Count); + Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); + Assert.Equal(2, results.Count(c => c.ParentBackNavigation != null)); + } - Assert.Equal(3, results.Count); - Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); - Assert.Equal(2, results.Count(c => c.ParentBackNavigationB != null)); - } + using (var context = contextFactory.CreateContext()) + { + var results = context.Children + .Select( + c => new + { + SelfReferenceBackNavigation + = EF.Property(c, "SelfReferenceBackNavigation"), + ParentBackNavigationB + = EF.Property( + EF.Property(c, "SelfReferenceBackNavigation"), + "ParentBackNavigation") + }) + .ToList(); - using (var context = new MyContext3409(_options)) - { - var results = context.Children - .Include(c => c.SelfReferenceBackNavigation) - .ThenInclude(c => c.ParentBackNavigation) - .ToList(); - - Assert.Equal(3, results.Count); - Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); - Assert.Equal(1, results.Count(c => c.ParentBackNavigation != null)); - } + Assert.Equal(3, results.Count); + Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); + Assert.Equal(2, results.Count(c => c.ParentBackNavigationB != null)); + } + + using (var context = contextFactory.CreateContext()) + { + var results = context.Children + .Include(c => c.SelfReferenceBackNavigation) + .ThenInclude(c => c.ParentBackNavigation) + .ToList(); + + Assert.Equal(3, results.Count); + Assert.Equal(2, results.Count(c => c.SelfReferenceBackNavigation != null)); + Assert.Equal(1, results.Count(c => c.ParentBackNavigation != null)); } } @@ -1264,296 +1049,236 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra .HasMany(c => (ICollection)c.SelfReferenceCollection) .WithOne(c => (Child3409)c.SelfReferenceBackNavigation); } - } - private interface IParent3409 - { - int Id { get; set; } + public void Seed() + { + var parent1 = new Parent3409(); - ICollection ChildCollection { get; set; } - } + var child1 = new Child3409(); + var child2 = new Child3409(); + var child3 = new Child3409(); - private interface IChild3409 - { - int Id { get; set; } + parent1.ChildCollection = new List { child1 }; + child1.SelfReferenceCollection = new List { child2, child3 }; - int? ParentBackNavigationId { get; set; } - IParent3409 ParentBackNavigation { get; set; } + Parents.AddRange(parent1); + Children.AddRange(child1, child2, child3); - ICollection SelfReferenceCollection { get; set; } - int? SelfReferenceBackNavigationId { get; set; } - IChild3409 SelfReferenceBackNavigation { get; set; } - } + SaveChanges(); + } - private class Parent3409 : IParent3409 - { - public int Id { get; set; } + public interface IParent3409 + { + int Id { get; set; } - public ICollection ChildCollection { get; set; } - } + ICollection ChildCollection { get; set; } + } - private class Child3409 : IChild3409 - { - public int Id { get; set; } + public interface IChild3409 + { + int Id { get; set; } - public int? ParentBackNavigationId { get; set; } - public IParent3409 ParentBackNavigation { get; set; } + int? ParentBackNavigationId { get; set; } + IParent3409 ParentBackNavigation { get; set; } - public ICollection SelfReferenceCollection { get; set; } - public int? SelfReferenceBackNavigationId { get; set; } - public IChild3409 SelfReferenceBackNavigation { get; set; } - } + ICollection SelfReferenceCollection { get; set; } + int? SelfReferenceBackNavigationId { get; set; } + IChild3409 SelfReferenceBackNavigation { get; set; } + } - private JetTestStore CreateDatabase3409() - { - return CreateTestStore( - () => new MyContext3409(_options), - context => - { - var parent1 = new Parent3409(); + public class Parent3409 : IParent3409 + { + public int Id { get; set; } - var child1 = new Child3409(); - var child2 = new Child3409(); - var child3 = new Child3409(); + public ICollection ChildCollection { get; set; } + } - parent1.ChildCollection = new List { child1 }; - child1.SelfReferenceCollection = new List { child2, child3 }; + public class Child3409 : IChild3409 + { + public int Id { get; set; } - context.Parents.AddRange(parent1); - context.Children.AddRange(child1, child2, child3); + public int? ParentBackNavigationId { get; set; } + public IParent3409 ParentBackNavigation { get; set; } - context.SaveChanges(); - }); + public ICollection SelfReferenceCollection { get; set; } + public int? SelfReferenceBackNavigationId { get; set; } + public IChild3409 SelfReferenceBackNavigation { get; set; } + } } #endregion - #region Bug3101 + #region Issue3101 [ConditionalFact] - public virtual void Repro3101_simple_coalesce1() + public virtual async Task Repro3101_simple_coalesce() { - using (CreateDatabase3101()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities.Include(e => e.Children) - join eRoot in ctx.Entities - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select eRootJoined ?? eVersion; - - var result = query.ToList(); - Assert.True(result.All(e => e.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities.Include(e => e.Children) + join eRoot in ctx.Entities + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select eRootJoined ?? eVersion; + + var result = query.ToList(); + + Assert.True(result.All(e => e.Children.Count > 0)); } - } - [ConditionalFact] - public virtual void Repro3101_simple_coalesce2() - { - using (CreateDatabase3101()) + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities - join eRoot in ctx.Entities.Include(e => e.Children) - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select eRootJoined ?? eVersion; - - var result = query.ToList(); - Assert.Equal(2, result.Count(e => e.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities + join eRoot in ctx.Entities.Include(e => e.Children) + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select eRootJoined ?? eVersion; + + var result = query.ToList(); + + Assert.Equal(2, result.Count(e => e.Children.Count > 0)); } - } - [ConditionalFact] - public virtual void Repro3101_simple_coalesce3() - { - using (CreateDatabase3101()) + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities.Include(e => e.Children) - join eRoot in ctx.Entities.Include(e => e.Children) - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select eRootJoined ?? eVersion; - - var result = query.ToList(); - Assert.True(result.All(e => e.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities.Include(e => e.Children) + join eRoot in ctx.Entities.Include(e => e.Children) + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select eRootJoined ?? eVersion; + + var result = query.ToList(); + + Assert.True(result.All(e => e.Children.Count > 0)); } } [ConditionalFact] - public virtual void Repro3101_complex_coalesce1() + public virtual async Task Repro3101_complex_coalesce() { - using (CreateDatabase3101()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities.Include(e => e.Children) - join eRoot in ctx.Entities - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select new { One = 1, Coalesce = eRootJoined ?? eVersion }; - - var result = query.ToList(); - Assert.True(result.All(e => e.Coalesce.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities.Include(e => e.Children) + join eRoot in ctx.Entities + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select new { One = 1, Coalesce = eRootJoined ?? eVersion }; + + var result = query.ToList(); + + Assert.True(result.All(e => e.Coalesce.Children.Count > 0)); } - } - [ConditionalFact] - public virtual void Repro3101_complex_coalesce2() - { - using (CreateDatabase3101()) + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities - join eRoot in ctx.Entities.Include(e => e.Children) - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select new { Root = eRootJoined, Coalesce = eRootJoined ?? eVersion }; - - var result = query.ToList(); - Assert.Equal(2, result.Count(e => e.Coalesce.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities + join eRoot in ctx.Entities.Include(e => e.Children) + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select new { Root = eRootJoined, Coalesce = eRootJoined ?? eVersion }; + + var result = query.ToList(); + + Assert.Equal(2, result.Count(e => e.Coalesce.Children.Count > 0)); } } [ConditionalFact] - public virtual void Repro3101_nested_coalesce1() + public virtual async Task Repro3101_nested_coalesce() { - using (CreateDatabase3101()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities - join eRoot in ctx.Entities.Include(e => e.Children) - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once ConstantNullCoalescingCondition - select new { One = 1, Coalesce = eRootJoined ?? (eVersion ?? eRootJoined) }; - - var result = query.ToList(); - Assert.Equal(2, result.Count(e => e.Coalesce.Children.Count > 0)); - } + var query = from eVersion in ctx.Entities + join eRoot in ctx.Entities.Include(e => e.Children) + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + // ReSharper disable once ConstantNullCoalescingCondition + select new { One = 1, Coalesce = eRootJoined ?? (eVersion ?? eRootJoined) }; + + var result = query.ToList(); + Assert.Equal(2, result.Count(e => e.Coalesce.Children.Count > 0)); } - } - [ConditionalFact] - public virtual void Repro3101_nested_coalesce2() - { - using (CreateDatabase3101()) + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities.Include(e => e.Children) - join eRoot in ctx.Entities - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once ConstantNullCoalescingCondition - select new - { - One = eRootJoined, - Two = 2, - Coalesce = eRootJoined ?? (eVersion ?? eRootJoined) - }; + var query = from eVersion in ctx.Entities.Include(e => e.Children) + join eRoot in ctx.Entities + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + // ReSharper disable once ConstantNullCoalescingCondition + select new + { + One = eRootJoined, + Two = 2, + Coalesce = eRootJoined ?? (eVersion ?? eRootJoined) + }; - var result = query.ToList(); - Assert.True(result.All(e => e.Coalesce.Children.Count > 0)); - } + var result = query.ToList(); + Assert.True(result.All(e => e.Coalesce.Children.Count > 0)); } } [ConditionalFact] - public virtual void Repro3101_conditional() + public virtual async Task Repro3101_conditional() { - using (CreateDatabase3101()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities.Include(e => e.Children) - join eRoot in ctx.Entities - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once MergeConditionalExpression + var query = from eVersion in ctx.Entities.Include(e => e.Children) + join eRoot in ctx.Entities + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + // ReSharper disable once MergeConditionalExpression #pragma warning disable IDE0029 // Use coalesce expression - select eRootJoined != null ? eRootJoined : eVersion; + select eRootJoined != null ? eRootJoined : eVersion; #pragma warning restore IDE0029 // Use coalesce expression - var result = query.ToList(); - Assert.True(result.All(e => e.Children.Count > 0)); - } + var result = query.ToList(); + Assert.True(result.All(e => e.Children.Count > 0)); } } [ConditionalFact] - public virtual void Repro3101_coalesce_tracking() + public virtual async Task Repro3101_coalesce_tracking() { - using (CreateDatabase3101()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext3101(_options)) - { - var query = from eVersion in ctx.Entities - join eRoot in ctx.Entities - on eVersion.RootEntityId equals eRoot.Id - into RootEntities - from eRootJoined in RootEntities.DefaultIfEmpty() - select new - { - eRootJoined, - eVersion, - foo = eRootJoined ?? eVersion - }; + var query = from eVersion in ctx.Entities + join eRoot in ctx.Entities + on eVersion.RootEntityId equals eRoot.Id + into RootEntities + from eRootJoined in RootEntities.DefaultIfEmpty() + select new + { + eRootJoined, + eVersion, + foo = eRootJoined ?? eVersion + }; - query.ToList(); + query.ToList(); - Assert.True(ctx.ChangeTracker.Entries().Any()); - } + Assert.True(ctx.ChangeTracker.Entries().Any()); } } - private JetTestStore CreateDatabase3101() - { - return CreateTestStore( - () => new MyContext3101(_options), - context => - { - var c11 = new Child3101 { Name = "c11" }; - var c12 = new Child3101 { Name = "c12" }; - var c13 = new Child3101 { Name = "c13" }; - var c21 = new Child3101 { Name = "c21" }; - var c22 = new Child3101 { Name = "c22" }; - var c31 = new Child3101 { Name = "c31" }; - var c32 = new Child3101 { Name = "c32" }; - - context.Children.AddRange(c11, c12, c13, c21, c22, c31, c32); - - var e1 = new Entity3101 { Id = 1, Children = new[] { c11, c12, c13 } }; - var e2 = new Entity3101 { Id = 2, Children = new[] { c21, c22 } }; - var e3 = new Entity3101 { Id = 3, Children = new[] { c31, c32 } }; - - e2.RootEntity = e1; - - context.Entities.AddRange(e1, e2, e3); - context.SaveChanges(); - }); - } - - private class MyContext3101 : DbContext + private class MyContext3101 : DbContext { public MyContext3101(DbContextOptions options) : base(options) @@ -1564,118 +1289,93 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra public DbSet Children { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); + + public void Seed() { - modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); + var c11 = new Child3101 { Name = "c11" }; + var c12 = new Child3101 { Name = "c12" }; + var c13 = new Child3101 { Name = "c13" }; + var c21 = new Child3101 { Name = "c21" }; + var c22 = new Child3101 { Name = "c22" }; + var c31 = new Child3101 { Name = "c31" }; + var c32 = new Child3101 { Name = "c32" }; + + Children.AddRange(c11, c12, c13, c21, c22, c31, c32); + + var e1 = new Entity3101 { Id = 1, Children = new[] { c11, c12, c13 } }; + var e2 = new Entity3101 { Id = 2, Children = new[] { c21, c22 } }; + var e3 = new Entity3101 { Id = 3, Children = new[] { c31, c32 } }; + + e2.RootEntity = e1; + + Entities.AddRange(e1, e2, e3); + SaveChanges(); } - } - private class Entity3101 - { - public Entity3101() + public class Entity3101 { - Children = new Collection(); - } + public Entity3101() + { + Children = new Collection(); + } - public int Id { get; set; } + public int Id { get; set; } - public int? RootEntityId { get; set; } + public int? RootEntityId { get; set; } - public Entity3101 RootEntity { get; set; } + public Entity3101 RootEntity { get; set; } - public ICollection Children { get; set; } - } + public ICollection Children { get; set; } + } - private class Child3101 - { - public int Id { get; set; } - public string Name { get; set; } + public class Child3101 + { + public int Id { get; set; } + public string Name { get; set; } + } } #endregion - #region Bug6986 + #region Issue6986 [ConditionalFact] - public virtual void Repro6986_can_query_base_type_when_derived_types_contain_shadow_properties() + public virtual async Task Repro6986() { - using (CreateDatabase6986()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new ReproContext6986(_options)) - { - var query = context.Contacts.ToList(); + // can_query_base_type_when_derived_types_contain_shadow_properties + var query = context.Contacts.ToList(); - Assert.Equal(4, query.Count); - Assert.Equal(2, query.OfType().Count()); - Assert.Single(query.OfType()); - } + Assert.Equal(4, query.Count); + Assert.Equal(2, query.OfType().Count()); + Assert.Single(query.OfType()); } - } - [ConditionalFact] - public virtual void Repro6986_can_include_dependent_to_principal_navigation_of_derived_type_with_shadow_fk() - { - using (CreateDatabase6986()) + using (var context = contextFactory.CreateContext()) { - using (var context = new ReproContext6986(_options)) - { - var query = context.Contacts.OfType().Include(e => e.ServiceOperator6986).ToList(); + // can_include_dependent_to_principal_navigation_of_derived_type_with_shadow_fk + var query = context.Contacts.OfType().Include(e => e.ServiceOperator6986) + .ToList(); - Assert.Single(query); - Assert.NotNull(query[0].ServiceOperator6986); - } + Assert.Single(query); + Assert.NotNull(query[0].ServiceOperator6986); } - } - [ConditionalFact] - public virtual void Repro6986_can_project_shadow_property_using_ef_property() - { - using (CreateDatabase6986()) + using (var context = contextFactory.CreateContext()) { - using (var context = new ReproContext6986(_options)) - { - var query = context.Contacts.OfType().Select( - c => new { c, Prop = EF.Property(c, "ServiceOperator6986Id") }).ToList(); + // can_project_shadow_property_using_ef_property + var query = context.Contacts.OfType().Select( + c => new { c, Prop = EF.Property(c, "ServiceOperator6986Id") }).ToList(); - Assert.Single(query); - Assert.Equal(1, query[0].Prop); - } + Assert.Single(query); + Assert.Equal(1, query[0].Prop); } } - private JetTestStore CreateDatabase6986() - { - return CreateTestStore( - () => new ReproContext6986(_options), - context => - { - context.ServiceOperators.Add(new ServiceOperator6986()); - context.Employers.AddRange( - new Employer6986 { Name = "UWE" }, - new Employer6986 { Name = "Hewlett Packard" }); - - context.SaveChanges(); - - context.Contacts.AddRange( - new ServiceOperatorContact6986 - { - UserName = "service.operator@esoterix.co.uk", - ServiceOperator6986 = context.ServiceOperators.OrderBy(o => o.Id).First() - }, - new EmployerContact6986 - { - UserName = "uwe@esoterix.co.uk", - Employer6986 = context.Employers.OrderBy(e => e.Id).First(e => e.Name == "UWE") - }, - new EmployerContact6986 - { - UserName = "hp@esoterix.co.uk", - Employer6986 = context.Employers.OrderBy(e => e.Id).First(e => e.Name == "Hewlett Packard") - }, - new Contact6986 { UserName = "noroles@esoterix.co.uk" }); - context.SaveChanges(); - }); - } - private class ReproContext6986 : DbContext { public ReproContext6986(DbContextOptions options) @@ -1688,185 +1388,148 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra public DbSet Employers { get; set; } public DbSet ServiceOperatorContacts { get; set; } public DbSet ServiceOperators { get; set; } - } - - private class EmployerContact6986 : Contact6986 - { - [Required] - public Employer6986 Employer6986 { get; set; } - } - - private class ServiceOperatorContact6986 : Contact6986 - { - [Required] - public ServiceOperator6986 ServiceOperator6986 { get; set; } - } - private class Contact6986 - { - public int Id { get; set; } - public string UserName { get; set; } - public bool IsPrimary { get; set; } - } - - private class Employer6986 - { - public int Id { get; set; } - public string Name { get; set; } - public List Contacts { get; set; } - } - - private class ServiceOperator6986 - { - public int Id { get; set; } - public List Contacts { get; set; } - } - - #endregion + public void Seed() + { + ServiceOperators.Add(new ServiceOperator6986()); + Employers.AddRange( + new Employer6986 { Name = "UWE" }, + new Employer6986 { Name = "Hewlett Packard" }); - #region Bug5456 + SaveChanges(); - [ConditionalFact] - public virtual void Repro5456_include_group_join_is_per_query_context() - { - using (CreateDatabase5456()) - { - Parallel.For( - 0, 10, i => + Contacts.AddRange( + new ServiceOperatorContact6986 { - using (var ctx = new MyContext5456(_options)) - { - var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ToList(); + UserName = "service.operator@esoterix.co.uk", + ServiceOperator6986 = ServiceOperators.OrderBy(o => o.Id).First() + }, + new EmployerContact6986 + { + UserName = "uwe@esoterix.co.uk", + Employer6986 = Employers.OrderBy(e => e.Id).First(e => e.Name == "UWE") + }, + new EmployerContact6986 + { + UserName = "hp@esoterix.co.uk", + Employer6986 = Employers.OrderBy(e => e.Id).First(e => e.Name == "Hewlett Packard") + }, + new Contact6986 { UserName = "noroles@esoterix.co.uk" }); + SaveChanges(); + } - Assert.Equal(198, result.Count); - } - }); + public class EmployerContact6986 : Contact6986 + { + [Required] + public Employer6986 Employer6986 { get; set; } } - } - [ConditionalFact] - public virtual async Task Repro5456_include_group_join_is_per_query_context_async() - { - using (CreateDatabase5456()) + public class ServiceOperatorContact6986 : Contact6986 { - await Task.WhenAll( - Enumerable.Range(0, 10) - .Select( - async i => - { - using (var ctx = new MyContext5456(_options)) - { - var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ToListAsync(); + [Required] + public ServiceOperator6986 ServiceOperator6986 { get; set; } + } - Assert.Equal(198, result.Count); - } - })); + public class Contact6986 + { + public int Id { get; set; } + public string UserName { get; set; } + public bool IsPrimary { get; set; } } - } - [ConditionalFact] - public virtual void Repro5456_multiple_include_group_join_is_per_query_context() - { - using (CreateDatabase5456()) + public class Employer6986 { - Parallel.For( - 0, 10, i => - { - using (var ctx = new MyContext5456(_options)) - { - var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).Include(x => x.Comments).ToList(); + public int Id { get; set; } + public string Name { get; set; } + public List Contacts { get; set; } + } - Assert.Equal(198, result.Count); - } - }); + public class ServiceOperator6986 + { + public int Id { get; set; } + public List Contacts { get; set; } } } + #endregion + + #region Issue5456 + [ConditionalFact] - public virtual async Task Repro5456_multiple_include_group_join_is_per_query_context_async() + public virtual async Task Repro5456_include_group_join_is_per_query_context() { - using (CreateDatabase5456()) - { - await Task.WhenAll( - Enumerable.Range(0, 10) - .Select( - async i => - { - using (var ctx = new MyContext5456(_options)) - { - var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).Include(x => x.Comments) - .ToListAsync(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - Assert.Equal(198, result.Count); - } - })); - } + Parallel.For( + 0, 10, i => + { + using var ctx = contextFactory.CreateContext(); + var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ToList(); + + Assert.Equal(198, result.Count); + }); + + Parallel.For( + 0, 10, i => + { + using var ctx = contextFactory.CreateContext(); + var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).Include(x => x.Comments).ToList(); + + Assert.Equal(198, result.Count); + }); + + Parallel.For( + 0, 10, i => + { + using var ctx = contextFactory.CreateContext(); + var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ThenInclude(b => b.Author).ToList(); + + Assert.Equal(198, result.Count); + }); } [ConditionalFact] - public virtual void Repro5456_multi_level_include_group_join_is_per_query_context() + public virtual async Task Repro5456_include_group_join_is_per_query_context_async() { - using (CreateDatabase5456()) - { - Parallel.For( - 0, 10, i => - { - using (var ctx = new MyContext5456(_options)) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + await Task.WhenAll( + Enumerable.Range(0, 10) + .Select( + async i => { - var result = ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ThenInclude(b => b.Author).ToList(); + using var ctx = contextFactory.CreateContext(); + var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ToListAsync(); Assert.Equal(198, result.Count); - } - }); - } - } + })); - [ConditionalFact] - public virtual async Task Repro5456_multi_level_include_group_join_is_per_query_context_async() - { - using (CreateDatabase5456()) - { - await Task.WhenAll( - Enumerable.Range(0, 10) - .Select( - async i => - { - using (var ctx = new MyContext5456(_options)) - { - var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ThenInclude(b => b.Author) - .ToListAsync(); + await Task.WhenAll( + Enumerable.Range(0, 10) + .Select( + async i => + { + using var ctx = contextFactory.CreateContext(); + var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).Include(x => x.Comments) + .ToListAsync(); - Assert.Equal(198, result.Count); - } - })); - } - } + Assert.Equal(198, result.Count); + })); - private JetTestStore CreateDatabase5456() - { - return CreateTestStore( - () => new MyContext5456(_options), - context => - { - for (var i = 0; i < 100; i++) - { - context.Add( - new Blog5456 - { - Posts = new List - { - new Post5456 { Comments = new List { new Comment5456(), new Comment5456() } }, - new Post5456() - }, - Author = new Author5456() - }); - } + await Task.WhenAll( + Enumerable.Range(0, 10) + .Select( + async i => + { + using var ctx = contextFactory.CreateContext(); + var result = await ctx.Posts.Where(x => x.Blog.Id > 1).Include(x => x.Blog).ThenInclude(b => b.Author) + .ToListAsync(); - context.SaveChanges(); - }); + Assert.Equal(198, result.Count); + })); } - private class MyContext5456 : DbContext + protected class MyContext5456 : DbContext { public MyContext5456(DbContextOptions options) : base(options) @@ -1877,71 +1540,74 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra public DbSet Posts { get; set; } public DbSet Comments { get; set; } public DbSet Authors { get; set; } - } - private class Blog5456 - { - public int Id { get; set; } - public List Posts { get; set; } - public Author5456 Author { get; set; } - } + public void Seed() + { + for (var i = 0; i < 100; i++) + { + Add( + new Blog5456 + { + Posts = new List { new() { Comments = new List { new(), new() } }, new() }, + Author = new Author5456() + }); + } - private class Author5456 - { - public int Id { get; set; } - public List Blogs { get; set; } - } + SaveChanges(); + } - private class Post5456 - { - public int Id { get; set; } - public Blog5456 Blog { get; set; } - public List Comments { get; set; } - } + public class Blog5456 + { + public int Id { get; set; } + public List Posts { get; set; } + public Author5456 Author { get; set; } + } - private class Comment5456 - { - public int Id { get; set; } - public Post5456 Blog { get; set; } + public class Author5456 + { + public int Id { get; set; } + public List Blogs { get; set; } + } + + public class Post5456 + { + public int Id { get; set; } + public Blog5456 Blog { get; set; } + public List Comments { get; set; } + } + + public class Comment5456 + { + public int Id { get; set; } + public Post5456 Blog { get; set; } + } } #endregion - #region Bug7359 + #region Issue7359 [ConditionalFact] - public virtual void Discriminator_type_is_handled_correctly_in_materialization_bug_7359() + public virtual async Task Discriminator_type_is_handled_correctly() { - using (CreateDatabase7359()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext7359(_options)) - { - var query = ctx.Products.OfType().ToList(); + var query = ctx.Products.OfType().ToList(); - Assert.Single(query); - } + Assert.Single(query); } - } - [ConditionalFact] - public virtual void Discriminator_type_is_handled_correctly_with_is_operator_bug_7359() - { - using (CreateDatabase7359()) + using (var ctx = contextFactory.CreateContext()) { - using (var ctx = new MyContext7359(_options)) - { - var query = ctx.Products.Where(p => p is SpecialProduct).ToList(); + var query = ctx.Products.Where(p => p is MyContext7359.SpecialProduct).ToList(); - Assert.Single(query); - } + Assert.Single(query); } } - private class SpecialProduct : Product - { - } - - private class MyContext7359 : DbContext + protected class MyContext7359 : DbContext { public MyContext7359(DbContextOptions options) : base(options) @@ -1958,60 +1624,41 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra .HasValue(0) .HasValue(1); } - } - private JetTestStore CreateDatabase7359() - { - return CreateTestStore( - () => new MyContext7359(_options), - context => - { - context.Add( - new Product { Name = "Product1" }); - context.Add( - new SpecialProduct { Name = "SpecialProduct" }); - context.SaveChanges(); - }); - } + public void Seed() + { + Add(new Product { Name = "Product1" }); + Add(new SpecialProduct { Name = "SpecialProduct" }); + SaveChanges(); + } - #endregion + public class Product + { + public int Id { get; set; } - #region Bug7312 + public string Name { get; set; } + } - [ConditionalFact] - public virtual void Reference_include_on_derived_type_with_sibling_works_bug_7312() - { - using (CreateDatabase7312()) + public class SpecialProduct : Product { - using (var context = new MyContext7312(_options)) - { - var query = context.Proposal.OfType().Include(l => l.LeaveType).ToList(); - - Assert.Single(query); - } } } - private class Proposal7312 - { - public int Id { get; set; } - } + #endregion - private class ProposalCustom7312 : Proposal7312 - { - public string Name { get; set; } - } + #region Issue7312 - private class ProposalLeave7312 : Proposal7312 + [ConditionalFact] + public virtual async Task Reference_include_on_derived_type_with_sibling_works_Issue_7312() { - public DateTime LeaveStart { get; set; } - public virtual ProposalLeaveType7312 LeaveType { get; set; } - } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - private class ProposalLeaveType7312 - { - public int Id { get; set; } - public ICollection ProposalLeaves { get; set; } + using (var context = contextFactory.CreateContext()) + { + var query = context.Proposal.OfType().Include(l => l.LeaveType).ToList(); + + Assert.Single(query); + } } private class MyContext7312 : DbContext @@ -2024,54 +1671,55 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra public DbSet Proposal { get; set; } public DbSet ProposalCustoms { get; set; } public DbSet ProposalLeaves { get; set; } - } - - private JetTestStore CreateDatabase7312() - { - return CreateTestStore( - () => new MyContext7312(_options), - context => - { - context.AddRange( - new Proposal7312(), - new ProposalCustom7312 { Name = "CustomProposal" }, - new ProposalLeave7312 { LeaveStart = DateTime.Now, LeaveType = new ProposalLeaveType7312() } - ); - context.SaveChanges(); - }); - } - #endregion + public void Seed() + { + AddRange( + new Proposal7312(), + new ProposalCustom7312 { Name = "CustomProposal" }, + new ProposalLeave7312 { LeaveStart = DateTime.Now, LeaveType = new ProposalLeaveType7312() } + ); + SaveChanges(); + } - #region Bug8282 + public class Proposal7312 + { + public int Id { get; set; } + } - [ConditionalFact] - public virtual void Entity_passed_to_DTO_constructor_works() - { - using (CreateDatabase8282()) + public class ProposalCustom7312 : Proposal7312 { - using (var context = new MyContext8282(_options)) - { - var query = context.Entity.Select(e => new EntityDto8282(e)).ToList(); + public string Name { get; set; } + } - Assert.Single(query); - } + public class ProposalLeave7312 : Proposal7312 + { + public DateTime LeaveStart { get; set; } + public virtual ProposalLeaveType7312 LeaveType { get; set; } } - } - private class Entity8282 - { - public int Id { get; set; } + public class ProposalLeaveType7312 + { + public int Id { get; set; } + public ICollection ProposalLeaves { get; set; } + } } - private class EntityDto8282 + #endregion + + #region Issue8282 + + [ConditionalFact] + public virtual async Task Entity_passed_to_DTO_constructor_works() { - public EntityDto8282(Entity8282 entity) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - Id = entity.Id; - } + var query = context.Entity.Select(e => new MyContext8282.EntityDto8282(e)).ToList(); - public int Id { get; } + Assert.Single(query); + } } private class MyContext8282 : DbContext @@ -2082,137 +1730,101 @@ Queen of the Andals and the Rhoynar and the First Men, Khaleesi of the Great Gra } public DbSet Entity { get; set; } - } - - private JetTestStore CreateDatabase8282() - { - return CreateTestStore( - () => new MyContext8282(_options), - context => - { - context.AddRange( - new Entity8282() - ); - context.SaveChanges(); - }); - } - #endregion + public void Seed() + { + AddRange(new Entity8282()); + SaveChanges(); + } - #region Bug8538 + public class Entity8282 + { + public int Id { get; set; } + } - [ConditionalFact] - public virtual void Enum_has_flag_applies_explicit_cast_for_long_constant() - { - using (CreateDatabase8538()) + public class EntityDto8282 { - using (var context = new MyContext8538(_options)) + public EntityDto8282(Entity8282 entity) { - var query = context.Entity.Where(e => e.Permission.HasFlag(Permission.READ_WRITE)).ToList(); - - Assert.Single(query); - - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Permission`, `e`.`PermissionByte`, `e`.`PermissionShort` -FROM `Entity` AS `e` -WHERE (`e`.`Permission` BAND 17179869184) = 17179869184"); + Id = entity.Id; } + + public int Id { get; } } } + #endregion + + #region Issue8538 + [ConditionalFact] - public virtual void Enum_has_flag_does_not_apply_explicit_cast_for_non_constant() + public virtual async Task Enum_has_flag_applies_explicit_cast_for_constant() { - using (CreateDatabase8538()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8538(_options)) - { - var query = context.Entity.Where(e => e.Permission.HasFlag(e.Permission)).ToList(); + var query = context.Entity.Where(e => e.Permission.HasFlag(MyContext8538.Permission.READ_WRITE)).ToList(); - Assert.Equal(3, query.Count); + Assert.Single(query); - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Permission`, `e`.`PermissionByte`, `e`.`PermissionShort` -FROM `Entity` AS `e` -WHERE (`e`.`Permission` BAND `e`.`Permission`) = `e`.`Permission`"); - } + AssertSql( + """ +SELECT [e].[Id], [e].[Permission], [e].[PermissionByte], [e].[PermissionShort] +FROM [Entity] AS [e] +WHERE [e].[Permission] & CAST(17179869184 AS bigint) = CAST(17179869184 AS bigint) +"""); } - } - [ConditionalFact] - public virtual void Byte_enum_has_flag_does_not_apply_explicit_cast_for_non_constant() - { - using (CreateDatabase8538()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8538(_options)) - { - var query = context.Entity.Where(e => e.PermissionByte.HasFlag(e.PermissionByte)).ToList(); + ClearLog(); + var query = context.Entity.Where(e => e.PermissionShort.HasFlag(MyContext8538.PermissionShort.READ_WRITE)).ToList(); - Assert.Equal(3, query.Count); + Assert.Single(query); - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Permission`, `e`.`PermissionByte`, `e`.`PermissionShort` -FROM `Entity` AS `e` -WHERE (`e`.`PermissionByte` BAND `e`.`PermissionByte`) = `e`.`PermissionByte`"); - } + AssertSql( + """ +SELECT [e].[Id], [e].[Permission], [e].[PermissionByte], [e].[PermissionShort] +FROM [Entity] AS [e] +WHERE [e].[PermissionShort] & CAST(4 AS smallint) = CAST(4 AS smallint) +"""); } } [ConditionalFact] - public virtual void Enum_has_flag_applies_explicit_cast_for_short_constant() + public virtual async Task Enum_has_flag_does_not_apply_explicit_cast_for_non_constant() { - using (CreateDatabase8538()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8538(_options)) - { - var query = context.Entity.Where(e => e.PermissionShort.HasFlag(PermissionShort.READ_WRITE)).ToList(); + var query = context.Entity.Where(e => e.Permission.HasFlag(e.Permission)).ToList(); - Assert.Single(query); + Assert.Equal(3, query.Count); - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Permission`, `e`.`PermissionByte`, `e`.`PermissionShort` -FROM `Entity` AS `e` -WHERE (`e`.`PermissionShort` BAND 4) = 4"); - } + AssertSql( + """ +SELECT [e].[Id], [e].[Permission], [e].[PermissionByte], [e].[PermissionShort] +FROM [Entity] AS [e] +WHERE [e].[Permission] & [e].[Permission] = [e].[Permission] +"""); } - } - - private class Entity8538 - { - public int Id { get; set; } - public Permission Permission { get; set; } - public PermissionByte PermissionByte { get; set; } - public PermissionShort PermissionShort { get; set; } - } - [Flags] -#pragma warning disable CA2217 // Do not mark enums with FlagsAttribute - private enum PermissionByte : byte -#pragma warning restore CA2217 // Do not mark enums with FlagsAttribute - { - NONE = 1, - READ_ONLY = 2, - READ_WRITE = 4 - } + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.Entity.Where(e => e.PermissionByte.HasFlag(e.PermissionByte)).ToList(); - [Flags] -#pragma warning disable CA2217 // Do not mark enums with FlagsAttribute - private enum PermissionShort : short -#pragma warning restore CA2217 // Do not mark enums with FlagsAttribute - { - NONE = 1, - READ_ONLY = 2, - READ_WRITE = 4 - } + Assert.Equal(3, query.Count); - [Flags] -#pragma warning disable CA2217 // Do not mark enums with FlagsAttribute - private enum Permission : long -#pragma warning restore CA2217 // Do not mark enums with FlagsAttribute - { - NONE = 0x01, - READ_ONLY = 0x02, - READ_WRITE = 0x400000000 // 36 bits + AssertSql( + """ +SELECT [e].[Id], [e].[Permission], [e].[PermissionByte], [e].[PermissionShort] +FROM [Entity] AS [e] +WHERE [e].[PermissionByte] & [e].[PermissionByte] = [e].[PermissionByte] +"""); + } } private class MyContext8538 : DbContext @@ -2223,229 +1835,275 @@ WHERE (`e`.`PermissionShort` BAND 4) = 4"); } public DbSet Entity { get; set; } - } - private JetTestStore CreateDatabase8538() - { - return CreateTestStore( - () => new MyContext8538(_options), - context => - { - context.AddRange( - new Entity8538 - { - Permission = Permission.NONE, - PermissionByte = PermissionByte.NONE, - PermissionShort = PermissionShort.NONE - }, - new Entity8538 - { - Permission = Permission.READ_ONLY, - PermissionByte = PermissionByte.READ_ONLY, - PermissionShort = PermissionShort.READ_ONLY - }, - new Entity8538 - { - Permission = Permission.READ_WRITE, - PermissionByte = PermissionByte.READ_WRITE, - PermissionShort = PermissionShort.READ_WRITE - } - ); - context.SaveChanges(); + public void Seed() + { + AddRange( + new Entity8538 + { + Permission = Permission.NONE, + PermissionByte = PermissionByte.NONE, + PermissionShort = PermissionShort.NONE + }, + new Entity8538 + { + Permission = Permission.READ_ONLY, + PermissionByte = PermissionByte.READ_ONLY, + PermissionShort = PermissionShort.READ_ONLY + }, + new Entity8538 + { + Permission = Permission.READ_WRITE, + PermissionByte = PermissionByte.READ_WRITE, + PermissionShort = PermissionShort.READ_WRITE + } + ); - ClearLog(); - }); + SaveChanges(); + } + + public class Entity8538 + { + public int Id { get; set; } + public Permission Permission { get; set; } + public PermissionByte PermissionByte { get; set; } + public PermissionShort PermissionShort { get; set; } + } + + [Flags] + public enum PermissionByte : byte + { + NONE = 1, + READ_ONLY = 2, + READ_WRITE = 4 + } + + [Flags] + public enum PermissionShort : short + { + NONE = 1, + READ_ONLY = 2, + READ_WRITE = 4 + } + + [Flags] + public enum Permission : long + { + NONE = 0x01, + READ_ONLY = 0x02, + READ_WRITE = 0x400000000 // 36 bits + } } #endregion - #region Bug8909 + #region Issue8909 [ConditionalFact] - public virtual void Variable_from_closure_is_parametrized() + public virtual async Task Variable_from_closure_is_parametrized() { - using (CreateDatabase8909()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8909(_options)) - { - context.Cache.Compact(1); + context.Cache.Compact(1); - var id = 1; - context.Entities.Where(c => c.Id == id).ToList(); - Assert.Equal(2, context.Cache.Count); + var id = 1; + context.Entities.Where(c => c.Id == id).ToList(); + Assert.Equal(2, context.Cache.Count); - id = 2; - context.Entities.Where(c => c.Id == id).ToList(); - Assert.Equal(2, context.Cache.Count); + id = 2; + context.Entities.Where(c => c.Id == id).ToList(); + Assert.Equal(2, context.Cache.Count); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__id_0='1'")} + AssertSql( +""" +@__id_0='1' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}", - // - $@"{AssertSqlHelper.Declaration("@__id_0='2'")} +WHERE `e`.`Id` = @__id_0 +""", +// +""" +@__id_0='2' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}"); - } +WHERE `e`.`Id` = @__id_0 +"""); } - } - [ConditionalFact] - public virtual void Variable_from_nested_closure_is_parametrized() - { - using (CreateDatabase8909()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8909(_options)) - { - context.Cache.Compact(1); + ClearLog(); + context.Cache.Compact(1); - var id = 0; - // ReSharper disable once AccessToModifiedClosure - Expression> whereExpression = c => c.Id == id; + var id = 0; + // ReSharper disable once AccessToModifiedClosure + Expression> whereExpression = c => c.Id == id; - id = 1; - context.Entities.Where(whereExpression).ToList(); - Assert.Equal(2, context.Cache.Count); + id = 1; + context.Entities.Where(whereExpression).ToList(); + Assert.Equal(2, context.Cache.Count); - id = 2; - context.Entities.Where(whereExpression).ToList(); - Assert.Equal(2, context.Cache.Count); + id = 2; + context.Entities.Where(whereExpression).ToList(); + Assert.Equal(2, context.Cache.Count); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__id_0='1'")} + AssertSql( +""" +@__id_0='1' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}", - // - $@"{AssertSqlHelper.Declaration("@__id_0='2'")} +WHERE `e`.`Id` = @__id_0 +""", +// +""" +@__id_0='2' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}"); - } +WHERE `e`.`Id` = @__id_0 +"""); } - } - [ConditionalFact] - public virtual void Variable_from_multi_level_nested_closure_is_parametrized() - { - using (CreateDatabase8909()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8909(_options)) - { - context.Cache.Compact(1); + ClearLog(); + context.Cache.Compact(1); - var id = 0; - // ReSharper disable once AccessToModifiedClosure - Expression> whereExpression = c => c.Id == id; - Expression> containsExpression = - c => context.Entities.Where(whereExpression).Select(e => e.Id).Contains(c.Id); + var id = 0; + // ReSharper disable once AccessToModifiedClosure + Expression> whereExpression = c => c.Id == id; + Expression> containsExpression = + c => context.Entities.Where(whereExpression).Select(e => e.Id).Contains(c.Id); - id = 1; - context.Entities.Where(containsExpression).ToList(); - Assert.Equal(2, context.Cache.Count); + id = 1; + context.Entities.Where(containsExpression).ToList(); + Assert.Equal(2, context.Cache.Count); - id = 2; - context.Entities.Where(containsExpression).ToList(); - Assert.Equal(2, context.Cache.Count); + id = 2; + context.Entities.Where(containsExpression).ToList(); + Assert.Equal(2, context.Cache.Count); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__id_0='1'")} + AssertSql( +""" +@__id_0='1' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` WHERE `e`.`Id` IN ( SELECT `e0`.`Id` FROM `Entities` AS `e0` - WHERE `e0`.`Id` = {AssertSqlHelper.Parameter("@__id_0")} -)", - // - $@"{AssertSqlHelper.Declaration("@__id_0='2'")} + WHERE `e0`.`Id` = @__id_0 +) +""", +// +""" +@__id_0='2' SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` WHERE `e`.`Id` IN ( SELECT `e0`.`Id` FROM `Entities` AS `e0` - WHERE `e0`.`Id` = {AssertSqlHelper.Parameter("@__id_0")} -)"); - } + WHERE `e0`.`Id` = @__id_0 +) +"""); } } [ConditionalFact] - public virtual void Relational_command_cache_creates_new_entry_when_parameter_nullability_changes() + public virtual async Task Relational_command_cache_creates_new_entry_when_parameter_nullability_changes() { - using (CreateDatabase8909()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8909(_options)) - { - context.Cache.Compact(1); + context.Cache.Compact(1); - var name = "A"; + var name = "A"; - context.Entities.Where(e => e.Name == name).ToList(); - Assert.Equal(2, context.Cache.Count); + context.Entities.Where(e => e.Name == name).ToList(); + Assert.Equal(2, context.Cache.Count); - name = null; - context.Entities.Where(e => e.Name == name).ToList(); - Assert.Equal(3, context.Cache.Count); + name = null; + context.Entities.Where(e => e.Name == name).ToList(); + Assert.Equal(3, context.Cache.Count); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__name_0='A' (Size = 255)")} + AssertSql( +""" +@__name_0='A' (Size = 255) SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Name` = {AssertSqlHelper.Parameter("@__name_0")}", - // - $@"SELECT `e`.`Id`, `e`.`Name` +WHERE `e`.`Name` = @__name_0 +""", +// +""" +SELECT `e`.`Id`, `e`.`Name` FROM `Entities` AS `e` -WHERE `e`.`Name` IS NULL"); - } +WHERE `e`.`Name` IS NULL +"""); } } [ConditionalFact] - public virtual void Query_cache_entries_are_evicted_as_necessary() + public virtual async Task Query_cache_entries_are_evicted_as_necessary() { - using (CreateDatabase8909()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8909(_options)) - { - context.Cache.Compact(1); - Assert.Equal(0, context.Cache.Count); - - var entityParam = Expression.Parameter(typeof(Entity8909), "e"); - var idPropertyInfo = context.Model.FindEntityType((typeof(Entity8909))) - .FindProperty(nameof(Entity8909.Id)) - .PropertyInfo; - for (var i = 0; i < 1100; i++) - { - var conditionBody = Expression.Equal( - Expression.MakeMemberAccess(entityParam, idPropertyInfo), - Expression.Constant(i)); - var whereExpression = Expression.Lambda>(conditionBody, entityParam); - context.Entities.Where(whereExpression).GetEnumerator(); - } + context.Cache.Compact(1); + Assert.Equal(0, context.Cache.Count); - Assert.True(context.Cache.Count <= 1024); + var entityParam = Expression.Parameter(typeof(MyContext8909.Entity8909), "e"); + var idPropertyInfo = context.Model.FindEntityType((typeof(MyContext8909.Entity8909))) + .FindProperty(nameof(MyContext8909.Entity8909.Id)) + .PropertyInfo; + for (var i = 0; i < 1100; i++) + { + var conditionBody = Expression.Equal( + Expression.MakeMemberAccess(entityParam, idPropertyInfo), + Expression.Constant(i)); + var whereExpression = Expression.Lambda>(conditionBody, entityParam); + context.Entities.Where(whereExpression).GetEnumerator(); } + + Assert.True(context.Cache.Count <= 1024); } } - private JetTestStore CreateDatabase8909() + [ConditionalFact] + public virtual async Task Explicitly_compiled_query_does_not_add_cache_entry() { - return CreateTestStore( - () => new MyContext8909(_options), - context => ClearLog()); + var parameter = Expression.Parameter(typeof(MyContext8909.Entity8909)); + var predicate = Expression.Lambda>( + Expression.MakeBinary( + ExpressionType.Equal, + Expression.PropertyOrField(parameter, "Id"), + Expression.Constant(1)), + parameter); + var query = EF.CompileQuery((MyContext8909 context) => context.Set().SingleOrDefault(predicate)); + + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + context.Cache.Compact(1); + Assert.Equal(0, context.Cache.Count); + + query(context); + + // 1 entry for RelationalCommandCache + Assert.Equal(1, context.Cache.Count); + } } - private class MyContext8909 : DbContext + protected class MyContext8909 : DbContext { public MyContext8909(DbContextOptions options) : base(options) @@ -2460,130 +2118,75 @@ WHERE `e`.`Name` IS NULL"); { var compiledQueryCache = this.GetService(); - return (MemoryCache)typeof(CompiledQueryCache).GetTypeInfo() + return (MemoryCache)typeof(CompiledQueryCache) .GetField("_memoryCache", BindingFlags.Instance | BindingFlags.NonPublic) ?.GetValue(compiledQueryCache); } } - } - private class Entity8909 - { - public int Id { get; set; } - public string Name { get; set; } + public class Entity8909 + { + public int Id { get; set; } + public string Name { get; set; } + } } #endregion - #region Bug9202/9210 + #region Issue9202/9210 [ConditionalFact] - public void Include_collection_for_entity_with_owned_type_works() + public async Task Include_collection_for_entity_with_owned_type_works() { - using (CreateDatabase9202()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9202(_options)) - { - var query = context.Movies.Include(m => m.Cast); - var result = query.ToList(); + var query = context.Movies.Include(m => m.Cast); + var result = query.ToList(); - Assert.Single(result); - Assert.Equal(3, result[0].Cast.Count); - Assert.NotNull(result[0].Details); - Assert.True(result[0].Cast.All(a => a.Details != null)); + Assert.Single(result); + Assert.Equal(3, result[0].Cast.Count); + Assert.NotNull(result[0].Details); + Assert.True(result[0].Cast.All(a => a.Details != null)); - AssertSql( - $@"SELECT `m`.`Id`, `m`.`Title`, `t`.`Id`, `t`.`Details_Info`, `t1`.`Id`, `t1`.`Movie9202Id`, `t1`.`Name`, `t1`.`Id0`, `t1`.`Details_Info` + AssertSql( +""" +SELECT `m`.`Id`, `m`.`Title`, `m`.`Details_Info`, `m`.`Details_Rating`, `a`.`Id`, `a`.`Movie9202Id`, `a`.`Name`, `a`.`Details_Info`, `a`.`Details_Rating` FROM `Movies` AS `m` -LEFT JOIN ( - SELECT `m0`.`Id`, `m0`.`Details_Info`, `m1`.`Id` AS `Id0` - FROM `Movies` AS `m0` - INNER JOIN `Movies` AS `m1` ON `m0`.`Id` = `m1`.`Id` - WHERE `m0`.`Details_Info` IS NOT NULL -) AS `t` ON `m`.`Id` = `t`.`Id` -LEFT JOIN ( - SELECT `a`.`Id`, `a`.`Movie9202Id`, `a`.`Name`, `t0`.`Id` AS `Id0`, `t0`.`Details_Info` - FROM `Actors` AS `a` - LEFT JOIN ( - SELECT `a0`.`Id`, `a0`.`Details_Info`, `a1`.`Id` AS `Id0` - FROM `Actors` AS `a0` - INNER JOIN `Actors` AS `a1` ON `a0`.`Id` = `a1`.`Id` - WHERE `a0`.`Details_Info` IS NOT NULL - ) AS `t0` ON `a`.`Id` = `t0`.`Id` -) AS `t1` ON `m`.`Id` = `t1`.`Movie9202Id` -ORDER BY `m`.`Id`, `t1`.`Id`"); - } +LEFT JOIN `Actors` AS `a` ON `m`.`Id` = `a`.`Movie9202Id` +ORDER BY `m`.`Id` +"""); } - } - [ConditionalFact] - public void Include_collection_for_entity_with_owned_type_works_string() - { - using (CreateDatabase9202()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9202(_options)) - { - var query = context.Movies.Include("Cast"); - var result = query.ToList(); + var query = context.Movies.Include("Cast"); + var result = query.ToList(); - Assert.Single(result); - Assert.Equal(3, result[0].Cast.Count); - Assert.NotNull(result[0].Details); - Assert.True(result[0].Cast.All(a => a.Details != null)); + Assert.Single(result); + Assert.Equal(3, result[0].Cast.Count); + Assert.NotNull(result[0].Details); + Assert.True(result[0].Cast.All(a => a.Details != null)); - AssertSql( - $@"SELECT `m`.`Id`, `m`.`Title`, `t`.`Id`, `t`.`Details_Info`, `t1`.`Id`, `t1`.`Movie9202Id`, `t1`.`Name`, `t1`.`Id0`, `t1`.`Details_Info` + AssertSql( +""" +SELECT `m`.`Id`, `m`.`Title`, `m`.`Details_Info`, `m`.`Details_Rating`, `a`.`Id`, `a`.`Movie9202Id`, `a`.`Name`, `a`.`Details_Info`, `a`.`Details_Rating` FROM `Movies` AS `m` -LEFT JOIN ( - SELECT `m0`.`Id`, `m0`.`Details_Info`, `m1`.`Id` AS `Id0` - FROM `Movies` AS `m0` - INNER JOIN `Movies` AS `m1` ON `m0`.`Id` = `m1`.`Id` - WHERE `m0`.`Details_Info` IS NOT NULL -) AS `t` ON `m`.`Id` = `t`.`Id` -LEFT JOIN ( - SELECT `a`.`Id`, `a`.`Movie9202Id`, `a`.`Name`, `t0`.`Id` AS `Id0`, `t0`.`Details_Info` - FROM `Actors` AS `a` - LEFT JOIN ( - SELECT `a0`.`Id`, `a0`.`Details_Info`, `a1`.`Id` AS `Id0` - FROM `Actors` AS `a0` - INNER JOIN `Actors` AS `a1` ON `a0`.`Id` = `a1`.`Id` - WHERE `a0`.`Details_Info` IS NOT NULL - ) AS `t0` ON `a`.`Id` = `t0`.`Id` -) AS `t1` ON `m`.`Id` = `t1`.`Movie9202Id` -ORDER BY `m`.`Id`, `t1`.`Id`"); - } +LEFT JOIN `Actors` AS `a` ON `m`.`Id` = `a`.`Movie9202Id` +ORDER BY `m`.`Id` +""", +// +""" +SELECT `m`.`Id`, `m`.`Title`, `m`.`Details_Info`, `m`.`Details_Rating`, `a`.`Id`, `a`.`Movie9202Id`, `a`.`Name`, `a`.`Details_Info`, `a`.`Details_Rating` +FROM `Movies` AS `m` +LEFT JOIN `Actors` AS `a` ON `m`.`Id` = `a`.`Movie9202Id` +ORDER BY `m`.`Id` +"""); } } - private JetTestStore CreateDatabase9202() - { - return CreateTestStore( - () => new MyContext9202(_options), - context => - { - var av = new Actor9202 { Name = "Alicia Vikander", Details = new Details9202 { Info = "Best actor ever made" } }; - var oi = new Actor9202 { Name = "Oscar Isaac", Details = new Details9202 { Info = "Best actor ever made" } }; - var dg = new Actor9202 { Name = "Domhnall Gleeson", Details = new Details9202 { Info = "Best actor ever made" } }; - var em = new Movie9202 - { - Title = "Ex Machina", - Cast = new List - { - av, - oi, - dg - }, - Details = new Details9202 { Info = "Best movie ever made" } - }; - context.Actors.AddRange(av, oi, dg); - context.Movies.Add(em); - context.SaveChanges(); - - ClearLog(); - }); - } - - private class MyContext9202 : DbContext + protected class MyContext9202 : DbContext { public MyContext9202(DbContextOptions options) : base(options) @@ -2599,123 +2202,105 @@ ORDER BY `m`.`Id`, `t1`.`Id`"); modelBuilder.Entity().OwnsOne(m => m.Details); modelBuilder.Entity().OwnsOne(m => m.Details); } - } - private class Movie9202 - { - public int Id { get; set; } - public string Title { get; set; } + public void Seed() + { + var av = new Actor9202 { Name = "Alicia Vikander", Details = new Details9202 { Info = "Best actor ever made" } }; + var oi = new Actor9202 { Name = "Oscar Isaac", Details = new Details9202 { Info = "Best actor ever made" } }; + var dg = new Actor9202 { Name = "Domhnall Gleeson", Details = new Details9202 { Info = "Best actor ever made" } }; + var em = new Movie9202 + { + Title = "Ex Machina", + Cast = new List + { + av, + oi, + dg + }, + Details = new Details9202 { Info = "Best movie ever made" } + }; - public List Cast { get; set; } + Actors.AddRange(av, oi, dg); + Movies.Add(em); + SaveChanges(); + } - public Details9202 Details { get; set; } - } + public class Movie9202 + { + public int Id { get; set; } + public string Title { get; set; } - private class Actor9202 - { - public int Id { get; set; } - public string Name { get; set; } - public Details9202 Details { get; set; } - } + public List Cast { get; set; } - private class Details9202 - { - public string Info { get; set; } + public Details9202 Details { get; set; } + } + + public class Actor9202 + { + public int Id { get; set; } + public string Name { get; set; } + public Details9202 Details { get; set; } + } + + public class Details9202 + { + public string Info { get; set; } + public int Rating { get; set; } + } } #endregion - #region Bug9214 + #region Issue9214 [ConditionalFact] - public void Default_schema_applied_when_no_function_schema() + public async Task Default_schema_applied_when_no_function_schema() { - using (CreateDatabase9214()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9214(_options)) - { - var result = context.Widgets.Where(w => w.Val == 1).Select(w => MyContext9214.AddOne(w.Val)).Single(); + var result = context.Widgets.Where(w => w.Val == 1).Select(w => MyContext9214.AddOne(w.Val)).Single(); - Assert.Equal(2, result); + Assert.Equal(2, result); - AssertSql( - $@"SELECT TOP 2 `foo`.`AddOne`(`w`.`Val`) -FROM `foo`.`Widgets` AS `w` -WHERE `w`.`Val` = 1"); - } + AssertSql( + """ +SELECT TOP(2) [foo].[AddOne]([w].[Val]) +FROM [foo].[Widgets] AS [w] +WHERE [w].[Val] = 1 +"""); } - } - [ConditionalFact] - public void Default_schema_function_schema_overrides() - { - using (CreateDatabase9214()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9214(_options)) - { - var result = context.Widgets.Where(w => w.Val == 1).Select(w => MyContext9214.AddTwo(w.Val)).Single(); + ClearLog(); + var result = context.Widgets.Where(w => w.Val == 1).Select(w => MyContext9214.AddTwo(w.Val)).Single(); - Assert.Equal(3, result); + Assert.Equal(3, result); - AssertSql( - $@"SELECT TOP 2 `AddTwo`(`w`.`Val`) -FROM `foo`.`Widgets` AS `w` -WHERE `w`.`Val` = 1"); - } + AssertSql( + """ +SELECT TOP(2) [dbo].[AddTwo]([w].[Val]) +FROM [foo].[Widgets] AS [w] +WHERE [w].[Val] = 1 +"""); } } - private JetTestStore CreateDatabase9214() - { - return CreateTestStore( - () => new MyContext9214(_options), - context => - { - var w1 = new Widget9214 { Val = 1 }; - var w2 = new Widget9214 { Val = 2 }; - var w3 = new Widget9214 { Val = 3 }; - context.Widgets.AddRange(w1, w2, w3); - context.SaveChanges(); - - context.Database.ExecuteSqlRaw( - @"CREATE FUNCTION foo.AddOne (@num int) - RETURNS int - AS - BEGIN - return @num + 1 ; - END"); - - context.Database.ExecuteSqlRaw( - @"CREATE FUNCTION AddTwo (@num int) - RETURNS int - AS - BEGIN - return @num + 2 ; - END"); - - ClearLog(); - }); - } - - private class MyContext9214 : DbContext + protected class MyContext9214 : DbContext { public DbSet Widgets { get; set; } #pragma warning disable IDE0060 // Remove unused parameter public static int AddOne(int num) - { - throw new Exception(); - } + => throw new Exception(); public static int AddTwo(int num) - { - throw new Exception(); - } + => throw new Exception(); public static int AddThree(int num) - { - throw new Exception(); - } + => throw new Exception(); #pragma warning restore IDE0060 // Remove unused parameter public MyContext9214(DbContextOptions options) @@ -2730,57 +2315,86 @@ WHERE `w`.`Val` = 1"); modelBuilder.Entity().ToTable("Widgets", "foo"); modelBuilder.HasDbFunction(typeof(MyContext9214).GetMethod(nameof(AddOne))); - modelBuilder.HasDbFunction(typeof(MyContext9214).GetMethod(nameof(AddTwo))).HasSchema(null); + modelBuilder.HasDbFunction(typeof(MyContext9214).GetMethod(nameof(AddTwo))).HasSchema("dbo"); } - } - - private class Widget9214 - { - public int Id { get; set; } - public int Val { get; set; } - } - #endregion + public void Seed() + { + var w1 = new Widget9214 { Val = 1 }; + var w2 = new Widget9214 { Val = 2 }; + var w3 = new Widget9214 { Val = 3 }; + Widgets.AddRange(w1, w2, w3); + SaveChanges(); - #region Bug9277 + Database.ExecuteSqlRaw( + @"CREATE FUNCTION foo.AddOne (@num int) + RETURNS int + AS + BEGIN + return @num + 1 ; + END"); + + Database.ExecuteSqlRaw( + @"CREATE FUNCTION dbo.AddTwo (@num int) + RETURNS int + AS + BEGIN + return @num + 2 ; + END"); + } + + public class Widget9214 + { + public int Id { get; set; } + public int Val { get; set; } + } + } + + #endregion + + #region Issue9277 [ConditionalFact] - public virtual void From_sql_gets_value_of_out_parameter_in_stored_procedure() + public virtual async Task From_sql_gets_value_of_out_parameter_in_stored_procedure() { - using (CreateDatabase9277()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9277(_options)) + var valueParam = new OleDbParameter { - var valueParam = new OleDbParameter - { - ParameterName = "Value", - Value = 0, - Direction = ParameterDirection.Output, - OleDbType = OleDbType.Integer - }; + ParameterName = "Value", + Value = 0, + Direction = ParameterDirection.Output, + OleDbType = OleDbType.Integer + }; - Assert.Equal(0, valueParam.Value); + Assert.Equal(0, valueParam.Value); - var blogs = context.Blogs.FromSqlRaw( - "`GetPersonAndVoteCount` @id, @Value out", - new OleDbParameter { ParameterName = "id", Value = 1 }, - valueParam) - .ToList(); + var blogs = context.Blogs.FromSqlRaw( + "[dbo].[GetPersonAndVoteCount] @id, @Value out", + new OleDbParameter { ParameterName = "id", Value = 1 }, + valueParam) + .ToList(); - Assert.Single(blogs); - Assert.Equal(1, valueParam.Value); - } + Assert.Single(blogs); + Assert.Equal(1, valueParam.Value); } } - private JetTestStore CreateDatabase9277() + protected class MyContext9277 : DbContext { - return CreateTestStore( - () => new MyContext9277(_options), - context => - { - context.Database.ExecuteSqlRaw( - @"CREATE PROCEDURE `GetPersonAndVoteCount` + public MyContext9277(DbContextOptions options) + : base(options) + { + } + + public DbSet Blogs { get; set; } + + public void Seed() + { + Database.ExecuteSqlRaw( + @"CREATE PROCEDURE [dbo].[GetPersonAndVoteCount] ( @id int, @Value int OUTPUT @@ -2788,117 +2402,65 @@ WHERE `w`.`Val` = 1"); AS BEGIN SELECT @Value = SomeValue - FROM Blogs + FROM dbo.Blogs WHERE Id = @id; SELECT * - FROM Blogs + FROM dbo.Blogs WHERE Id = @id; END"); - context.AddRange( - new Blog9277 { SomeValue = 1 }, - new Blog9277 { SomeValue = 2 }, - new Blog9277 { SomeValue = 3 } - ); - - context.SaveChanges(); + AddRange( + new Blog9277 { SomeValue = 1 }, + new Blog9277 { SomeValue = 2 }, + new Blog9277 { SomeValue = 3 } + ); - ClearLog(); - }); - } + SaveChanges(); + } - private class MyContext9277 : DbContext - { - public MyContext9277(DbContextOptions options) - : base(options) + public class Blog9277 { + public int Id { get; set; } + public int SomeValue { get; set; } } - - public DbSet Blogs { get; set; } - } - - private class Blog9277 - { - public int Id { get; set; } - public int SomeValue { get; set; } } #endregion - #region Bug9038 + #region Issue9038 [ConditionalFact] - public virtual async Task Include_collection_optional_reference_collection_9038() + public virtual async Task Include_collection_optional_reference_collection() { - using (CreateDatabase9038()) - { - using (var context = new MyContext9038(_options)) - { - var result = await context.People.OfType() - .Include(m => m.Students) - .ThenInclude(m => m.Family) - .ThenInclude(m => m.Members) - .ToListAsync(); - - Assert.Equal(2, result.Count); - Assert.True(result.All(r => r.Students.Count > 0)); - } - } - } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - [ConditionalFact] - public async Task Include_optional_reference_collection_another_collection() - { - using (CreateDatabase9038()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9038(_options)) - { - var result = await context.Set() - .Include(m => m.Family.Members) - .Include(m => m.Students) - .ToListAsync(); - - Assert.Equal(2, result.Count); - Assert.True(result.All(r => r.Students.Count > 0)); - Assert.Null(result.Single(t => t.Name == "Ms. Frizzle").Family); - Assert.NotNull(result.Single(t => t.Name == "Mr. Garrison").Family); - } - } - } - - private abstract class Person9038 - { - public int Id { get; set; } - - public string Name { get; set; } - - public int? TeacherId { get; set; } - - public PersonFamily9038 Family { get; set; } - } - - private class PersonKid9038 : Person9038 - { - public int Grade { get; set; } - - public PersonTeacher9038 Teacher { get; set; } - } - - private class PersonTeacher9038 : Person9038 - { - public ICollection Students { get; set; } - } + var result = await context.People.OfType() + .Include(m => m.Students) + .ThenInclude(m => m.Family) + .ThenInclude(m => m.Members) + .ToListAsync(); - private class PersonFamily9038 - { - public int Id { get; set; } + Assert.Equal(2, result.Count); + Assert.True(result.All(r => r.Students.Count > 0)); + } - public string LastName { get; set; } + using (var context = contextFactory.CreateContext()) + { + var result = await context.Set() + .Include(m => m.Family.Members) + .Include(m => m.Students) + .ToListAsync(); - public ICollection Members { get; set; } + Assert.Equal(2, result.Count); + Assert.True(result.All(r => r.Students.Count > 0)); + Assert.Null(result.Single(t => t.Name == "Ms. Frizzle").Family); + Assert.NotNull(result.Single(t => t.Name == "Mr. Garrison").Family); + } } - private class MyContext9038 : DbContext + protected class MyContext9038 : DbContext { public MyContext9038(DbContextOptions options) : base(options) @@ -2929,4500 +2491,7947 @@ BEGIN .OnDelete(DeleteBehavior.Restrict); }); } - } - private JetTestStore CreateDatabase9038() - { - return CreateTestStore( - () => new MyContext9038(_options), - context => + public void Seed() + { + var famalies = new List { new() { LastName = "Garrison" }, new() { LastName = "Cartman" } }; + var teachers = new List + { + new() { Name = "Ms. Frizzle" }, new() { Name = "Mr. Garrison", Family = famalies[0] } + }; + var students = new List + { + new() { - var famalies = new List - { - new PersonFamily9038 { LastName = "Garrison" }, new PersonFamily9038 { LastName = "Cartman" } - }; - var teachers = new List - { - new PersonTeacher9038 { Name = "Ms. Frizzle" }, - new PersonTeacher9038 { Name = "Mr. Garrison", Family = famalies[0] } - }; - var students = new List - { - new PersonKid9038 - { - Name = "Arnold", - Grade = 2, - Teacher = teachers[0] - }, - new PersonKid9038 - { - Name = "Eric", - Grade = 4, - Teacher = teachers[1], - Family = famalies[1] - } - }; + Name = "Arnold", + Grade = 2, + Teacher = teachers[0] + }, + new() + { + Name = "Eric", + Grade = 4, + Teacher = teachers[1], + Family = famalies[1] + } + }; + + People.AddRange(teachers); + People.AddRange(students); + SaveChanges(); + } - context.People.AddRange(teachers); - context.People.AddRange(students); - context.SaveChanges(); + public abstract class Person9038 + { + public int Id { get; set; } - ClearLog(); - }); - } + public string Name { get; set; } - #endregion + public int? TeacherId { get; set; } - #region Bug9735 + public PersonFamily9038 Family { get; set; } + } - [ConditionalFact] - // TODO: Convert to test in IncludeTestBase once issue #9742 is fixed - public virtual void Repro9735() - { - using (CreateDatabase9735()) + public class PersonKid9038 : Person9038 { - using (var context = new MyContext9735(_options)) - { - var result = context.Customers - .Include(b => b.Orders) - .OrderBy(b => b.Address.Id > 0) - .ThenBy(b => b.CustomerDetails != null ? b.CustomerDetails.Name : string.Empty) - .Take(2) - .ToList(); + public int Grade { get; set; } - Assert.Single(result); + public PersonTeacher9038 Teacher { get; set; } + } - AssertSql( - $@"{AssertSqlHelper.Declaration("@__p_0='2'")} + public class PersonTeacher9038 : Person9038 + { + public ICollection Students { get; set; } + } -SELECT `t`.`Id`, `t`.`AddressId`, `t`.`CustomerDetailsId`, `t`.`Name`, `t`.`Id0`, `o`.`Id`, `o`.`CustomerId`, `o`.`Name` -FROM ( - SELECT TOP {AssertSqlHelper.Parameter("@__p_0")} `c`.`Id`, `c`.`AddressId`, `c`.`CustomerDetailsId`, `c`.`Name`, `a`.`Id` AS `Id0`, IIF(`a`.`Id` > 0, 1, 0) AS `c`, CASE - WHEN `c0`.`Id` IS NOT NULL THEN `c0`.`Name` - ELSE '' - END AS `c0` - FROM `Customers` AS `c` - INNER JOIN `Address9735` AS `a` ON `c`.`AddressId` = `a`.`Id` - LEFT JOIN `CustomerDetails9735` AS `c0` ON `c`.`CustomerDetailsId` = `c0`.`Id` - ORDER BY IIF(`a`.`Id` > 0, 1, 0), CASE - WHEN `c0`.`Id` IS NOT NULL THEN `c0`.`Name` - ELSE '' - END -) AS `t` -LEFT JOIN `Order9735` AS `o` ON `t`.`Id` = `o`.`CustomerId` -ORDER BY `t`.`c`, `t`.`c0`, `t`.`Id`, `t`.`Id0`, `o`.`Id`"); - } + public class PersonFamily9038 + { + public int Id { get; set; } + + public string LastName { get; set; } + + public ICollection Members { get; set; } } } - private class Address9735 - { - public int Id { get; set; } - public string Name { get; set; } - } + #endregion - private class Customer9735 - { - public int Id { get; set; } - public string Name { get; set; } - public int AddressId { get; set; } - public virtual Address9735 Address { get; set; } - public virtual List Orders { get; set; } - public virtual CustomerDetails9735 CustomerDetails { get; set; } - } + #region Issue9468 - private class Order9735 + [ConditionalFact] + public virtual async Task Conditional_expression_with_conditions_does_not_collapse_if_nullable_bool() { - public int Id { get; set; } - public string Name { get; set; } - public int CustomerId { get; set; } - public virtual Customer9735 Customer { get; set; } - } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - private class CustomerDetails9735 - { - public int Id { get; set; } - public string Name { get; set; } + using (var context = contextFactory.CreateContext()) + { + var query = context.Carts.Select( + t => new { Processing = t.Configuration != null ? !t.Configuration.Processed : (bool?)null }).ToList(); + + Assert.Single(query.Where(t => t.Processing == null)); + Assert.Single(query.Where(t => t.Processing == true)); + Assert.Single(query.Where(t => t.Processing == false)); + + AssertSql( +""" +SELECT IIF(`c0`.`Id` IS NOT NULL, IIF(`c0`.`Processed` <> TRUE, TRUE, FALSE), NULL) AS `Processing` +FROM `Carts` AS `c` +LEFT JOIN `Configuration9468` AS `c0` ON `c`.`ConfigurationId` = `c0`.`Id` +"""); + } } - private class MyContext9735 : DbContext + protected class MyContext9468 : DbContext { - public MyContext9735(DbContextOptions options) + public MyContext9468(DbContextOptions options) : base(options) { } - public DbSet Customers { get; set; } - } + public DbSet Carts { get; set; } - private JetTestStore CreateDatabase9735() - { - return CreateTestStore( - () => new MyContext9735(_options), - context => - { - context.AddRange( - new Address9735 { Name = "An A" }, - new Customer9735 { Name = "A B", AddressId = 1 } - ); - context.SaveChanges(); + public void Seed() + { + AddRange( + new Cart9468(), + new Cart9468 { Configuration = new Configuration9468 { Processed = true } }, + new Cart9468 { Configuration = new Configuration9468() } + ); - ClearLog(); - }); + SaveChanges(); + } + + public class Cart9468 + { + public int Id { get; set; } + public int? ConfigurationId { get; set; } + public Configuration9468 Configuration { get; set; } + } + + public class Configuration9468 + { + public int Id { get; set; } + public bool Processed { get; set; } + } } #endregion - #region Bug9892 + #region Issue10635 - [ConditionalFact(Skip = "Issue #17068")] - public virtual void GroupJoin_to_parent_with_no_child_works_9892() + [ConditionalFact] + public async Task Include_with_order_by_on_interface_key() { - using (CreateDatabase9892()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9892(_options)) - { - var results = ( - from p in context.Parents - join c in ( - from x in context.Children - select new { x.ParentId, OtherParent = x.OtherParent.Name }) - on p.Id equals c.ParentId into child - select new - { - ParentId = p.Id, - ParentName = p.Name, - Children = child.Select(c => c.OtherParent) - }).ToList(); + var query = context.Parents.Include(p => p.Children).OrderBy(p => p.Id).ToList(); - Assert.Equal(3, results.Count); - Assert.Single(results.Where(t => !t.Children.Any())); - } + AssertSql( +""" +SELECT `p`.`Id`, `p`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Parent10635Id`, `c`.`ParentId` +FROM `Parents` AS `p` +LEFT JOIN `Children` AS `c` ON `p`.`Id` = `c`.`Parent10635Id` +ORDER BY `p`.`Id` +"""); } - } - private JetTestStore CreateDatabase9892() - { - return CreateTestStore( - () => new MyContext9892(_options), - context => - { - context.Parents.Add( - new Parent9892 { Name = "Parent1" }); - context.Parents.Add( - new Parent9892 { Name = "Parent2" }); - context.Parents.Add( - new Parent9892 { Name = "Parent3" }); - - context.OtherParents.Add( - new OtherParent9892 { Name = "OtherParent1" }); - context.OtherParents.Add( - new OtherParent9892 { Name = "OtherParent2" }); - - context.SaveChanges(); - - context.Children.Add( - new Child9892 { ParentId = 1, OtherParentId = 1 }); - context.Children.Add( - new Child9892 { ParentId = 1, OtherParentId = 2 }); - context.Children.Add( - new Child9892 { ParentId = 2, OtherParentId = 1 }); - context.Children.Add( - new Child9892 { ParentId = 2, OtherParentId = 2 }); - - context.SaveChanges(); + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.Parents.OrderBy(p => p.Id).Select(p => p.Children.ToList()).ToList(); - ClearLog(); - }); + AssertSql( +""" +SELECT `p`.`Id`, `c`.`Id`, `c`.`Name`, `c`.`Parent10635Id`, `c`.`ParentId` +FROM `Parents` AS `p` +LEFT JOIN `Children` AS `c` ON `p`.`Id` = `c`.`Parent10635Id` +ORDER BY `p`.`Id` +"""); + } } - private class MyContext9892 : DbContext + private class MyContext10635 : DbContext { - public MyContext9892(DbContextOptions options) + public MyContext10635(DbContextOptions options) : base(options) { } - public DbSet Parents { get; set; } - public DbSet Children { get; set; } - public DbSet OtherParents { get; set; } - } + public DbSet Parents { get; set; } + public DbSet Children { get; set; } - private class Parent9892 - { - public int Id { get; set; } - public string Name { get; set; } - public List Children { get; set; } - } + public void Seed() + { + var c11 = new Child10635 { Name = "Child111" }; + var c12 = new Child10635 { Name = "Child112" }; + var c13 = new Child10635 { Name = "Child113" }; + var c21 = new Child10635 { Name = "Child121" }; - private class OtherParent9892 - { - public int Id { get; set; } - public string Name { get; set; } - } + var p1 = new Parent10635 { Name = "Parent1", Children = new[] { c11, c12, c13 } }; + var p2 = new Parent10635 { Name = "Parent2", Children = new[] { c21 } }; + Parents.AddRange(p1, p2); + Children.AddRange(c11, c12, c13, c21); + SaveChanges(); + } - private class Child9892 - { - public int Id { get; set; } - public int ParentId { get; set; } - public Parent9892 Parent { get; set; } - public int OtherParentId { get; set; } - public OtherParent9892 OtherParent { get; set; } + public interface IEntity10635 + { + int Id { get; set; } + } + + public class Parent10635 : IEntity10635 + { + public int Id { get; set; } + public string Name { get; set; } + public virtual ICollection Children { get; set; } + } + + public class Child10635 : IEntity10635 + { + public int Id { get; set; } + public string Name { get; set; } + public int ParentId { get; set; } + } } #endregion - #region Bug9468 + #region Issue10301 [ConditionalFact] - public virtual void Conditional_expression_with_conditions_does_not_collapse_if_nullable_bool() + public virtual async Task MultiContext_query_filter_test() { - using (CreateDatabase9468()) - { - using (var context = new MyContext9468(_options)) - { - var query = context.Carts.Select( - t => new { Processing = t.Configuration != null ? !t.Configuration.Processed : (bool?)null }).ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - Assert.Single(query.Where(t => t.Processing == null)); - Assert.Single(query.Where(t => t.Processing == true)); - Assert.Single(query.Where(t => t.Processing == false)); + using (var context = contextFactory.CreateContext()) + { + Assert.Empty(context.Blogs.ToList()); - AssertSql( - $@"SELECT CASE - WHEN `c0`.`Id` IS NOT NULL THEN IIF(`c0`.`Processed` <> True, 1, 0) - ELSE NULL -END AS `Processing` -FROM `Carts` AS `c` -LEFT JOIN `Configuration9468` AS `c0` ON `c`.`ConfigurationId` = `c0`.`Id`"); - } - } - } + context.Tenant = 1; + Assert.Single(context.Blogs.ToList()); - private JetTestStore CreateDatabase9468() - { - return CreateTestStore( - () => new MyContext9468(_options), - context => - { - context.AddRange( - new Cart9468(), - new Cart9468 { Configuration = new Configuration9468 { Processed = true } }, - new Cart9468 { Configuration = new Configuration9468() } - ); + context.Tenant = 2; + Assert.Equal(2, context.Blogs.Count()); - context.SaveChanges(); + AssertSql( +""" +@__ef_filter__Tenant_0='0' + +SELECT `b`.`Id`, `b`.`SomeValue` +FROM `Blogs` AS `b` +WHERE `b`.`SomeValue` = @__ef_filter__Tenant_0 +""", +// +""" +@__ef_filter__Tenant_0='1' + +SELECT `b`.`Id`, `b`.`SomeValue` +FROM `Blogs` AS `b` +WHERE `b`.`SomeValue` = @__ef_filter__Tenant_0 +""", +// +""" +@__ef_filter__Tenant_0='2' - ClearLog(); - }); +SELECT COUNT(*) +FROM `Blogs` AS `b` +WHERE `b`.`SomeValue` = @__ef_filter__Tenant_0 +"""); + } } - private class MyContext9468 : DbContext + protected class FilterContextBase10301 : DbContext { - public MyContext9468(DbContextOptions options) + public int Tenant { get; set; } + + public FilterContextBase10301(DbContextOptions options) : base(options) { } - public DbSet Carts { get; set; } - } + public DbSet Blogs { get; set; } - private class Cart9468 - { - public int Id { get; set; } - public int? ConfigurationId { get; set; } - public Configuration9468 Configuration { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasQueryFilter(e => e.SomeValue == Tenant); + + public void Seed() + { + AddRange( + new Blog10301 { SomeValue = 1 }, + new Blog10301 { SomeValue = 2 }, + new Blog10301 { SomeValue = 2 } + ); + + SaveChanges(); + } + + public class Blog10301 + { + public int Id { get; set; } + public int SomeValue { get; set; } + } } - private class Configuration9468 + protected class FilterContext10301 : FilterContextBase10301 { - public int Id { get; set; } - public bool Processed { get; set; } + public FilterContext10301(DbContextOptions options) + : base(options) + { + } } #endregion - #region Bug10635 + #region Issue11104 [ConditionalFact] - public void Include_with_order_by_on_interface_key() + public virtual async Task QueryBuffer_requirement_is_computed_when_querying_base_type_while_derived_type_has_shadow_prop() { - using (CreateDatabase10635()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext10635(_options)) - { - var query = context.Parents.Include(p => p.Children).OrderBy(p => p.Id).ToList(); + var query = context.Bases.ToList(); - AssertSql( - $@"SELECT `p`.`Id`, `p`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Parent10635Id`, `c`.`ParentId` -FROM `Parents` AS `p` -LEFT JOIN `Children` AS `c` ON `p`.`Id` = `c`.`Parent10635Id` -ORDER BY `p`.`Id`, `c`.`Id`"); - } + var derived1 = Assert.Single(query); + Assert.Equal(typeof(MyContext11104.Derived1), derived1.GetType()); + + AssertSql( +""" +SELECT `b`.`Id`, `b`.`IsTwo`, `b`.`MoreStuffId` +FROM `Bases` AS `b` +"""); } } - [ConditionalFact] - public void Correlated_collection_with_order_by_on_interface_key() + protected class MyContext11104 : DbContext { - using (CreateDatabase10635()) - { - using (var context = new MyContext10635(_options)) - { - var query = context.Parents.OrderBy(p => p.Id).Select(p => p.Children.ToList()).ToList(); + public DbSet Bases { get; set; } - AssertSql( - $@"SELECT `p`.`Id`, `c`.`Id`, `c`.`Name`, `c`.`Parent10635Id`, `c`.`ParentId` -FROM `Parents` AS `p` -LEFT JOIN `Children` AS `c` ON `p`.`Id` = `c`.`Parent10635Id` -ORDER BY `p`.`Id`, `c`.`Id`"); - } + public MyContext11104(DbContextOptions options) + : base(options) + { } - } - private JetTestStore CreateDatabase10635() - { - return CreateTestStore( - () => new MyContext10635(_options), - context => - { - var c11 = new Child10635 { Name = "Child111" }; - var c12 = new Child10635 { Name = "Child112" }; - var c13 = new Child10635 { Name = "Child113" }; - var c21 = new Child10635 { Name = "Child121" }; + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity() + .HasDiscriminator(x => x.IsTwo) + .HasValue(false) + .HasValue(true); - var p1 = new Parent10635 { Name = "Parent1", Children = new[] { c11, c12, c13 } }; - var p2 = new Parent10635 { Name = "Parent2", Children = new[] { c21 } }; - context.Parents.AddRange(p1, p2); - context.Children.AddRange(c11, c12, c13, c21); - context.SaveChanges(); + public void Seed() + { + AddRange( + new Derived1 { IsTwo = false } + ); - ClearLog(); - }); - } + SaveChanges(); + } - private class MyContext10635 : DbContext - { - public MyContext10635(DbContextOptions options) - : base(options) + public abstract class Base { + public int Id { get; set; } + public bool IsTwo { get; set; } } - public DbSet Parents { get; set; } - public DbSet Children { get; set; } - } - - private interface IEntity10635 - { - int Id { get; set; } - } + public class Derived1 : Base + { + public Stuff MoreStuff { get; set; } + } - private class Parent10635 : IEntity10635 - { - public int Id { get; set; } - public string Name { get; set; } - public virtual ICollection Children { get; set; } - } + public class Derived2 : Base + { + } - private class Child10635 : IEntity10635 - { - public int Id { get; set; } - public string Name { get; set; } - public int ParentId { get; set; } + public class Stuff + { + public int Id { get; set; } + } } #endregion - #region Bug10168 + #region Issue11818_11831 - [ConditionalFact(Skip = "issue #16400")] - public void Row_number_paging_with_owned_type() - { - using (var context = new MyContext10168(Fixture.TestSqlLoggerFactory)) - { - context.Database.EnsureClean(); - context.Add( - new Note { Text = "Foo Bar", User = new User10168 { Fullname = "Full1", Email = "abc@def.com" } }); + [ConditionalFact] + public virtual async Task GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination() + { + var contextFactory = await InitializeAsync( + onConfiguring: + o => o.ConfigureWarnings(w => w.Log(CoreEventId.FirstWithoutOrderByAndFilterWarning))); + + using (var context = contextFactory.CreateContext()) + { + var query = (from e in context.Set() + join a in context.Set() + on e.Id equals a.Id into grouping + from a in grouping.DefaultIfEmpty() + select new { ename = e.Name, aname = a.Name }) + .GroupBy(g => g.aname) + .Select( + g => new { g.Key, cnt = g.Count() + 5 }) + .ToList(); - context.SaveChanges(); - ClearLog(); + Assert.Empty(query); + + AssertSql( +""" +SELECT `t0`.`AnotherEntity11818_Name` AS `Key`, COUNT(*) + 5 AS `cnt` +FROM `Table` AS `t` +LEFT JOIN ( + SELECT `t1`.`Id`, `t1`.`Exists`, `t1`.`AnotherEntity11818_Name` + FROM `Table` AS `t1` + WHERE `t1`.`Exists` IS NOT NULL +) AS `t0` ON `t`.`Id` = IIF(`t0`.`Exists` IS NOT NULL, `t0`.`Id`, NULL) +GROUP BY `t0`.`AnotherEntity11818_Name` +"""); } - using (var context = new MyContext10168(Fixture.TestSqlLoggerFactory)) + using (var context = contextFactory.CreateContext()) { - var query = context.Note.Where(x => x.Text == "Foo Bar") - .Skip(0) - .Take(100) + ClearLog(); + var query = (from e in context.Set() + join a in context.Set() + on e.Id equals a.Id into grouping + from a in grouping.DefaultIfEmpty() + join m in context.Set() + on e.Id equals m.Id into grouping2 + from m in grouping2.DefaultIfEmpty() + select new { aname = a.Name, mname = m.Name }) + .GroupBy( + g => new { g.aname, g.mname }) + .Select( + g => new { MyKey = g.Key.aname, cnt = g.Count() + 5 }) .ToList(); - var result = Assert.Single(query); - Assert.NotNull(result.User); - Assert.Equal("Full1", result.User.Fullname); + Assert.Empty(query); AssertSql( - $@"{AssertSqlHelper.Declaration("@__p_0='?' (DbType = Int32)")} +""" +SELECT `t0`.`AnotherEntity11818_Name` AS `MyKey`, COUNT(*) + 5 AS `cnt` +FROM (`Table` AS `t` +LEFT JOIN ( + SELECT `t1`.`Id`, `t1`.`Exists`, `t1`.`AnotherEntity11818_Name` + FROM `Table` AS `t1` + WHERE `t1`.`Exists` IS NOT NULL +) AS `t0` ON `t`.`Id` = IIF(`t0`.`Exists` IS NOT NULL, `t0`.`Id`, NULL)) +LEFT JOIN ( + SELECT `t3`.`Id`, `t3`.`MaumarEntity11818_Exists`, `t3`.`MaumarEntity11818_Name` + FROM `Table` AS `t3` + WHERE `t3`.`MaumarEntity11818_Exists` IS NOT NULL +) AS `t2` ON `t`.`Id` = IIF(`t2`.`MaumarEntity11818_Exists` IS NOT NULL, `t2`.`Id`, NULL) +GROUP BY `t0`.`AnotherEntity11818_Name`, `t2`.`MaumarEntity11818_Name` +"""); + } -{AssertSqlHelper.Declaration("@__p_1='?' (DbType = Int32)")} + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = (from e in context.Set() + join a in context.Set() + on e.Id equals a.Id into grouping + from a in grouping.DefaultIfEmpty() + join m in context.Set() + on e.Id equals m.Id into grouping2 + from m in grouping2.DefaultIfEmpty() + select new { aname = a.Name, mname = m.Name }) + .OrderBy(g => g.aname) + .GroupBy(g => new { g.aname, g.mname }) + .Select(g => new { MyKey = g.Key.aname, cnt = g.Key.mname }).FirstOrDefault(); + + Assert.Null(query); -SELECT `t0`.`Id`, `t0`.`Text`, `t0`.`Id0`, `t0`.`User_Email`, `t0`.`User_Fullname` -FROM ( - SELECT `x`.`Id`, `x`.`Text`, `t`.`Id` AS `Id0`, `t`.`User_Email`, `t`.`User_Fullname`, ROW_NUMBER() OVER(ORDER BY @@RowCount) AS `__RowNumber__` - FROM `Note` AS `x` - LEFT JOIN ( - SELECT [x.User].* - FROM `Note` AS [x.User] - WHERE [x.User].`User_Fullname` IS NOT NULL OR [x.User].`User_Email` IS NOT NULL - ) AS `t` ON `x`.`Id` = `t`.`Id` - WHERE `x`.`Text` = 'Foo Bar' -) AS `t0` -WHERE (`t0`.`__RowNumber__` > {AssertSqlHelper.Parameter("@__p_0")}) AND (`t0`.`__RowNumber__` <= ({AssertSqlHelper.Parameter("@__p_0")} + {AssertSqlHelper.Parameter("@__p_1")}))"); + AssertSql( +""" +SELECT TOP 1 `t0`.`AnotherEntity11818_Name` AS `MyKey`, `t2`.`MaumarEntity11818_Name` AS `cnt` +FROM (`Table` AS `t` +LEFT JOIN ( + SELECT `t1`.`Id`, `t1`.`Exists`, `t1`.`AnotherEntity11818_Name` + FROM `Table` AS `t1` + WHERE `t1`.`Exists` IS NOT NULL +) AS `t0` ON `t`.`Id` = IIF(`t0`.`Exists` IS NOT NULL, `t0`.`Id`, NULL)) +LEFT JOIN ( + SELECT `t3`.`Id`, `t3`.`MaumarEntity11818_Exists`, `t3`.`MaumarEntity11818_Name` + FROM `Table` AS `t3` + WHERE `t3`.`MaumarEntity11818_Exists` IS NOT NULL +) AS `t2` ON `t`.`Id` = IIF(`t2`.`MaumarEntity11818_Exists` IS NOT NULL, `t2`.`Id`, NULL) +GROUP BY `t0`.`AnotherEntity11818_Name`, `t2`.`MaumarEntity11818_Name` +"""); } } - private class MyContext10168 : DbContext + protected class MyContext11818 : DbContext { - private readonly ILoggerFactory _loggerFactory; - - public MyContext10168(ILoggerFactory loggerFactory) + public MyContext11818(DbContextOptions options) + : base(options) { - _loggerFactory = loggerFactory; } - public DbSet Note { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + protected override void OnModelCreating(ModelBuilder modelBuilder) { - optionsBuilder - .UseLoggerFactory(_loggerFactory) - .EnableServiceProviderCaching(false) - .UseJet( - JetTestStore.CreateConnectionString("RowNumberPaging_Owned"), - TestEnvironment.DataAccessProviderFactory); + modelBuilder.Entity().ToTable("Table"); + modelBuilder.Entity().ToTable("Table"); + modelBuilder.Entity().ToTable("Table"); + + modelBuilder.Entity() + .HasOne() + .WithOne() + .HasForeignKey(b => b.Id); + + modelBuilder.Entity() + .HasOne() + .WithOne() + .HasForeignKey(b => b.Id); } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public class Entity11818 { - modelBuilder.Entity().OwnsOne(n => n.User).WithOwner().HasForeignKey(u => u.Id); + public int Id { get; set; } + public string Name { get; set; } } - } - - private class Note - { - [Key] - public Guid Id { get; set; } - public string Text { get; set; } - public User10168 User { get; set; } - } + public class AnotherEntity11818 + { + public int Id { get; set; } + public string Name { get; set; } + public bool Exists { get; set; } + } - private class User10168 - { - public Guid Id { get; set; } - public string Fullname { get; set; } - public string Email { get; set; } + public class MaumarEntity11818 + { + public int Id { get; set; } + public string Name { get; set; } + public bool Exists { get; set; } + } } #endregion - #region Bug10301 + #region Issue11803_11791 - [ConditionalFact(Skip = "issue #15364")] - public virtual void MultiContext_query_filter_test() + [ConditionalFact] + public virtual async Task Query_filter_with_db_set_should_not_block_other_filters() { - using (CreateDatabase10301()) - { - using (var context = new FilterContext10301(_options)) - { - Assert.Empty(context.Blogs.ToList()); - - context.Tenant = 1; - Assert.Single(context.Blogs.ToList()); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - context.Tenant = 2; - Assert.Equal(2, context.Blogs.Count()); - - AssertSql( - $@"{AssertSqlHelper.Declaration("@__ef_filter__Tenant_0='0'")} - -SELECT `e`.`Id`, `e`.`SomeValue` -FROM `Blogs` AS `e` -WHERE `e`.`SomeValue` = {AssertSqlHelper.Parameter("@__ef_filter__Tenant_0")}", - // - $@"{AssertSqlHelper.Declaration("@__ef_filter__Tenant_0='1'")} + using (var context = contextFactory.CreateContext()) + { + var query = context.Factions.ToList(); -SELECT `e`.`Id`, `e`.`SomeValue` -FROM `Blogs` AS `e` -WHERE `e`.`SomeValue` = {AssertSqlHelper.Parameter("@__ef_filter__Tenant_0")}", - // - $@"{AssertSqlHelper.Declaration("@__ef_filter__Tenant_0='2'")} + Assert.Empty(query); -SELECT COUNT(*) -FROM `Blogs` AS `e` -WHERE `e`.`SomeValue` = {AssertSqlHelper.Parameter("@__ef_filter__Tenant_0")}"); - } + AssertSql( +""" +SELECT `f`.`Id`, `f`.`Name` +FROM `Factions` AS `f` +WHERE EXISTS ( + SELECT 1 + FROM `Leaders` AS `l` + WHERE (`l`.`Name` LIKE 'Bran%') AND `l`.`Name` = 'Crach an Craite') +"""); } } - private JetTestStore CreateDatabase10301() + [ConditionalFact] + public virtual async Task Keyless_type_used_inside_defining_query() { - return CreateTestStore( - () => new FilterContext10301(_options), - context => - { - context.AddRange( - new Blog10301 { SomeValue = 1 }, - new Blog10301 { SomeValue = 2 }, - new Blog10301 { SomeValue = 2 } - ); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - context.SaveChanges(); + using (var context = contextFactory.CreateContext()) + { + var query = context.LeadersQuery.ToList(); - ClearLog(); - }); + Assert.Single(query); + + AssertSql( +""" +SELECT `t`.`Name` +FROM ( + SELECT `l`.`Name` + FROM `Leaders` AS `l` + WHERE (`l`.`Name` LIKE 'Bran' + '%' AND (LEFT(`l`.`Name`, LEN('Bran')) = 'Bran')) AND ((`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL) +) AS `t` +WHERE (`t`.`Name` <> 'Bar') OR `t`.`Name` IS NULL +"""); + } } - private class FilterContextBase10301 : DbContext + protected class MyContext11803 : DbContext { - public int Tenant { get; set; } + public DbSet Factions { get; set; } + public DbSet Leaders { get; set; } + public DbSet LeadersQuery { get; set; } - public FilterContextBase10301(DbContextOptions options) + public MyContext11803(DbContextOptions options) : base(options) { } - public DbSet Blogs { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().HasQueryFilter(e => e.SomeValue == Tenant); + modelBuilder.Entity().HasQueryFilter(l => l.Name.StartsWith("Bran")); // this one is ignored + modelBuilder.Entity().HasQueryFilter(f => Leaders.Any(l => l.Name == "Crach an Craite")); + + modelBuilder + .Entity() + .HasNoKey() + .ToSqlQuery( + """ +SELECT `t`.`Name` +FROM ( + SELECT `l`.`Name` + FROM `Leaders` AS `l` + WHERE (`l`.`Name` LIKE 'Bran' + '%' AND (LEFT(`l`.`Name`, LEN('Bran')) = 'Bran')) AND ((`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL) +) AS `t` +WHERE (`t`.`Name` <> 'Bar') OR `t`.`Name` IS NULL +"""); } - } - private class Blog10301 - { - public int Id { get; set; } - public int SomeValue { get; set; } - } + public void Seed() + { + var f1 = new Faction { Name = "Skeliege" }; + var f2 = new Faction { Name = "Monsters" }; + var f3 = new Faction { Name = "Nilfgaard" }; + var f4 = new Faction { Name = "Northern Realms" }; + var f5 = new Faction { Name = "Scioia'tael" }; - private class FilterContext10301 : FilterContextBase10301 - { - public FilterContext10301(DbContextOptions options) - : base(options) + var l11 = new Leader { Faction = f1, Name = "Bran Tuirseach" }; + var l12 = new Leader { Faction = f1, Name = "Crach an Craite" }; + var l13 = new Leader { Faction = f1, Name = "Eist Tuirseach" }; + var l14 = new Leader { Faction = f1, Name = "Harald the Cripple" }; + + Factions.AddRange(f1, f2, f3, f4, f5); + Leaders.AddRange(l11, l12, l13, l14); + + SaveChanges(); + } + + public class Faction + { + public int Id { get; set; } + public string Name { get; set; } + + public List Leaders { get; set; } + } + + public class Leader + { + public int Id { get; set; } + public string Name { get; set; } + public Faction Faction { get; set; } + } + + public class LeaderQuery { + public string Name { get; set; } } } #endregion - #region Bug11104 + #region Issue11923 [ConditionalFact] - public virtual void QueryBuffer_requirement_is_computed_when_querying_base_type_while_derived_type_has_shadow_prop() + public virtual async Task Collection_without_setter_materialized_correctly() { - using (CreateDatabase11104()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext11104(_options)) - { - var query = context.Bases.ToList(); + var query1 = context.Blogs + .Select( + b => new + { + Collection1 = b.Posts1, + Collection2 = b.Posts2, + Collection3 = b.Posts3 + }).ToList(); - var derived1 = Assert.Single(query); - Assert.Equal(typeof(Derived1), derived1.GetType()); + var query2 = context.Blogs + .Select( + b => new + { + Collection1 = b.Posts1.OrderBy(p => p.Id).First().Comments.Count, + Collection2 = b.Posts2.OrderBy(p => p.Id).First().Comments.Count, + Collection3 = b.Posts3.OrderBy(p => p.Id).First().Comments.Count + }).ToList(); - AssertSql( - $@"SELECT `b`.`Id`, `b`.`IsTwo`, `b`.`MoreStuffId` -FROM `Bases` AS `b` -WHERE `b`.`IsTwo` IN (False, True)"); - } + Assert.Throws( + () => context.Blogs + .Select( + b => new + { + Collection1 = b.Posts1.OrderBy(p => p.Id), + Collection2 = b.Posts2.OrderBy(p => p.Id), + Collection3 = b.Posts3.OrderBy(p => p.Id) + }).ToList()); } } - private JetTestStore CreateDatabase11104() - { - return CreateTestStore( - () => new MyContext11104(_options), - context => - { - context.AddRange( - new Derived1 { IsTwo = false } - ); - - context.SaveChanges(); - - ClearLog(); - }); - } - - private class MyContext11104 : DbContext + protected class MyContext11923 : DbContext { - public DbSet Bases { get; set; } + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Comments { get; set; } - public MyContext11104(DbContextOptions options) + public MyContext11923(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .HasDiscriminator(x => x.IsTwo) - .HasValue(false) - .HasValue(true); + modelBuilder.Entity( + b => + { + b.HasMany(e => e.Posts1).WithOne().HasForeignKey("BlogId1"); + b.HasMany(e => e.Posts2).WithOne().HasForeignKey("BlogId2"); + b.HasMany(e => e.Posts3).WithOne().HasForeignKey("BlogId3"); + }); + + modelBuilder.Entity(); } - } - private abstract class Base - { - public int Id { get; set; } - public bool IsTwo { get; set; } - } + public void Seed() + { + var p111 = new Post11923 { Name = "P111" }; + var p112 = new Post11923 { Name = "P112" }; + var p121 = new Post11923 { Name = "P121" }; + var p122 = new Post11923 { Name = "P122" }; + var p123 = new Post11923 { Name = "P123" }; + var p131 = new Post11923 { Name = "P131" }; - private class Derived1 : Base - { - public Stuff MoreStuff { get; set; } - } + var p211 = new Post11923 { Name = "P211" }; + var p212 = new Post11923 { Name = "P212" }; + var p221 = new Post11923 { Name = "P221" }; + var p222 = new Post11923 { Name = "P222" }; + var p223 = new Post11923 { Name = "P223" }; + var p231 = new Post11923 { Name = "P231" }; - private class Derived2 : Base - { - } + var b1 = new Blog11923 { Name = "B1" }; + var b2 = new Blog11923 { Name = "B2" }; - private class Stuff - { - public int Id { get; set; } - } + b1.Posts1.AddRange(new[] { p111, p112 }); + b1.Posts2.AddRange(new[] { p121, p122, p123 }); + b1.Posts3.Add(p131); - #endregion + b2.Posts1.AddRange(new[] { p211, p212 }); + b2.Posts2.AddRange(new[] { p221, p222, p223 }); + b2.Posts3.Add(p231); - #region Bug11818_11831 + Blogs.AddRange(b1, b2); + Posts.AddRange(p111, p112, p121, p122, p123, p131, p211, p212, p221, p222, p223, p231); + SaveChanges(); + } - [ConditionalFact] - public virtual void GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination() - { - using (CreateDatabase11818()) + public class Blog11923 { - using (var context = new MyContext11818(_options)) + public Blog11923() { - var query = (from e in context.Set() - join a in context.Set() - on e.Id equals a.Id into grouping - from a in grouping.DefaultIfEmpty() - select new { ename = e.Name, aname = a.Name }) - .GroupBy(g => g.aname) - .Select( - g => new { g.Key, cnt = g.Count() + 5 }) - .ToList(); + Posts1 = new List(); + Posts2 = new CustomCollection11923(); + Posts3 = new HashSet(); + } - AssertSql( - $@"SELECT `t2`.`Name` AS `Key`, COUNT(*) + 5 AS `cnt` -FROM `Table` AS `t` -LEFT JOIN ( - SELECT `t0`.`Id`, `t0`.`Name`, `t1`.`Id` AS `Id0` - FROM `Table` AS `t0` - INNER JOIN `Table` AS `t1` ON `t0`.`Id` = `t1`.`Id` - WHERE `t0`.`Name` IS NOT NULL -) AS `t2` ON `t`.`Id` = `t2`.`Id` -GROUP BY `t2`.`Name`"); + public Blog11923(List posts1, CustomCollection11923 posts2, HashSet posts3) + { + Posts1 = posts1; + Posts2 = posts2; + Posts3 = posts3; } + + public int Id { get; set; } + public string Name { get; set; } + + public List Posts1 { get; } + public CustomCollection11923 Posts2 { get; } + public HashSet Posts3 { get; } } - } - [ConditionalFact] - public virtual void GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination_2() - { - using (CreateDatabase11818()) + public class Post11923 { - using (var context = new MyContext11818(_options)) - { - var query = (from e in context.Set() - join a in context.Set() - on e.Id equals a.Id into grouping - from a in grouping.DefaultIfEmpty() - join m in context.Set() - on e.Id equals m.Id into grouping2 - from m in grouping2.DefaultIfEmpty() - select new { aname = a.Name, mname = m.Name }) - .GroupBy( - g => new { g.aname, g.mname }) - .Select( - g => new { MyKey = g.Key.aname, cnt = g.Count() + 5 }) - .ToList(); + public int Id { get; set; } + public string Name { get; set; } - AssertSql( - $@"SELECT `t2`.`Name` AS `MyKey`, COUNT(*) + 5 AS `cnt` -FROM `Table` AS `t` -LEFT JOIN ( - SELECT `t0`.`Id`, `t0`.`Name`, `t1`.`Id` AS `Id0` - FROM `Table` AS `t0` - INNER JOIN `Table` AS `t1` ON `t0`.`Id` = `t1`.`Id` - WHERE `t0`.`Name` IS NOT NULL -) AS `t2` ON `t`.`Id` = `t2`.`Id` -LEFT JOIN ( - SELECT `t3`.`Id`, `t3`.`MaumarEntity11818_Name`, `t4`.`Id` AS `Id0` - FROM `Table` AS `t3` - INNER JOIN `Table` AS `t4` ON `t3`.`Id` = `t4`.`Id` - WHERE `t3`.`MaumarEntity11818_Name` IS NOT NULL -) AS `t5` ON `t`.`Id` = `t5`.`Id` -GROUP BY `t2`.`Name`, `t5`.`MaumarEntity11818_Name`"); - } + public List Comments { get; set; } } - } - [ConditionalFact(Skip = "Issue #11871")] - public virtual void GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination_4() - { - using (CreateDatabase11818()) + public class Comment11923 { - using (var context = new MyContext11818(_options)) - { - var query = (from e in context.Set() - join a in context.Set() - on e.Id equals a.Id into grouping - from a in grouping.DefaultIfEmpty() - join m in context.Set() - on e.Id equals m.Id into grouping2 - from m in grouping2.DefaultIfEmpty() - select new { aname = a.Name, mname = m.Name }) - .OrderBy(g => g.aname) - .GroupBy( - g => new { g.aname, g.mname }).FirstOrDefault() - .Select( - g => new { MyKey = g.aname, cnt = g.mname }) - .ToList(); + public int Id { get; set; } + } - AssertSql( - $@""); - } + public class CustomCollection11923 : List + { } } - private JetTestStore CreateDatabase11818() + #endregion + + #region Issue11885 + + [ConditionalFact] + public virtual async Task Average_with_cast() { - return CreateTestStore( - () => new MyContext11818(_options), - context => - { - context.SaveChanges(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - ClearLog(); - }); - } + using (var context = contextFactory.CreateContext()) + { + var prices = context.Prices.ToList(); - private class MyContext11818 : DbContext + ClearLog(); + + Assert.Equal(prices.Average(e => e.Price), context.Prices.Average(e => e.Price)); + Assert.Equal(prices.Average(e => e.IntColumn), context.Prices.Average(e => e.IntColumn)); + Assert.Equal(prices.Average(e => e.NullableIntColumn), context.Prices.Average(e => e.NullableIntColumn)); + Assert.Equal(prices.Average(e => e.LongColumn), context.Prices.Average(e => e.LongColumn)); + Assert.Equal(prices.Average(e => e.NullableLongColumn), context.Prices.Average(e => e.NullableLongColumn)); + Assert.Equal(prices.Average(e => e.FloatColumn), context.Prices.Average(e => e.FloatColumn)); + Assert.Equal(prices.Average(e => e.NullableFloatColumn), context.Prices.Average(e => e.NullableFloatColumn)); + Assert.Equal(prices.Average(e => e.DoubleColumn), context.Prices.Average(e => e.DoubleColumn)); + Assert.Equal(prices.Average(e => e.NullableDoubleColumn), context.Prices.Average(e => e.NullableDoubleColumn)); + Assert.Equal(prices.Average(e => e.DecimalColumn), context.Prices.Average(e => e.DecimalColumn)); + Assert.Equal(prices.Average(e => e.NullableDecimalColumn), context.Prices.Average(e => e.NullableDecimalColumn)); + + AssertSql( +""" +SELECT AVG(`p`.`Price`) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(CDBL(`p`.`IntColumn`)) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(IIF(`p`.`NullableIntColumn` IS NULL, NULL, CDBL(`p`.`NullableIntColumn`))) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(CDBL(`p`.`LongColumn`)) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(IIF(`p`.`NullableLongColumn` IS NULL, NULL, CDBL(`p`.`NullableLongColumn`))) +FROM `Prices` AS `p` +""", +// +""" +SELECT CSNG(AVG(`p`.`FloatColumn`)) +FROM `Prices` AS `p` +""", +// +""" +SELECT CSNG(AVG(`p`.`NullableFloatColumn`)) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(`p`.`DoubleColumn`) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(`p`.`NullableDoubleColumn`) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(`p`.`DecimalColumn`) +FROM `Prices` AS `p` +""", +// +""" +SELECT AVG(`p`.`NullableDecimalColumn`) +FROM `Prices` AS `p` +"""); + } + } + + protected class MyContext11885 : DbContext { - public MyContext11818(DbContextOptions options) + public DbSet Prices { get; set; } + + public MyContext11885(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().ToTable("Table"); - modelBuilder.Entity().ToTable("Table"); - modelBuilder.Entity().ToTable("Table"); + => modelBuilder.Entity( + b => + { + b.Property(e => e.Price).HasColumnType("DECIMAL(18, 8)"); + b.Property(e => e.DecimalColumn).HasColumnType("DECIMAL(18, 2)"); + b.Property(e => e.NullableDecimalColumn).HasColumnType("DECIMAL(18, 2)"); + }); - modelBuilder.Entity() - .HasOne() - .WithOne() - .HasForeignKey(b => b.Id); + public void Seed() + { + AddRange( + new Price11885 + { + IntColumn = 1, + NullableIntColumn = 1, + LongColumn = 1000, + NullableLongColumn = 1000, + FloatColumn = 0.1F, + NullableFloatColumn = 0.1F, + DoubleColumn = 0.000001, + NullableDoubleColumn = 0.000001, + DecimalColumn = 1.0m, + NullableDecimalColumn = 1.0m, + Price = 0.00112000m + }, + new Price11885 + { + IntColumn = 2, + NullableIntColumn = 2, + LongColumn = 2000, + NullableLongColumn = 2000, + FloatColumn = 0.2F, + NullableFloatColumn = 0.2F, + DoubleColumn = 0.000002, + NullableDoubleColumn = 0.000002, + DecimalColumn = 2.0m, + NullableDecimalColumn = 2.0m, + Price = 0.00232111m + }, + new Price11885 + { + IntColumn = 3, + LongColumn = 3000, + FloatColumn = 0.3F, + DoubleColumn = 0.000003, + DecimalColumn = 3.0m, + Price = 0.00345223m + } + ); - modelBuilder.Entity() - .HasOne() - .WithOne() - .HasForeignKey(b => b.Id); + SaveChanges(); } - } - - private class Entity11818 - { - public int Id { get; set; } - public string Name { get; set; } - } - - private class AnotherEntity11818 - { - public int Id { get; set; } - public string Name { get; set; } - } - private class MaumarEntity11818 - { - public int Id { get; set; } - public string Name { get; set; } + public class Price11885 + { + public int Id { get; set; } + public int IntColumn { get; set; } + public int? NullableIntColumn { get; set; } + public long LongColumn { get; set; } + public long? NullableLongColumn { get; set; } + public float FloatColumn { get; set; } + public float? NullableFloatColumn { get; set; } + public double DoubleColumn { get; set; } + public double? NullableDoubleColumn { get; set; } + public decimal DecimalColumn { get; set; } + public decimal? NullableDecimalColumn { get; set; } + public decimal Price { get; set; } + } } #endregion - #region Bug11803_11791 + #region Issue12582 - [ConditionalFact(Skip = "Issue #15364")] - public virtual void Query_filter_with_db_set_should_not_block_other_filters() + [ConditionalFact] + public virtual async Task Include_collection_with_OfType_base() { - using (CreateDatabase11803()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext11803(_options)) - { - context.Factions.ToList(); + var query = context.Employees + .Include(i => i.Devices) + .OfType() + .ToList(); - AssertSql( - $@"SELECT `f`.`Id`, `f`.`Name` -FROM `ConditionalFactions` AS `f` -WHERE EXISTS ( - SELECT 1 - FROM `Leaders` AS `l` - WHERE `l`.`Name` = 'Crach an Craite')"); - } + Assert.Single(query); + + var employee = (MyContext12582.Employee12582)query[0]; + Assert.Equal(2, employee.Devices.Count); } - } - [ConditionalFact(Skip = "Issue#13361")] - public virtual void Keyless_type_used_inside_defining_query() - { - using (CreateDatabase11803()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext11803(_options)) - { - context.LeadersQuery.ToList(); + var query = context.Employees + .Select(e => e.Devices.Where(d => d.Device != "foo").Cast()) + .ToList(); - AssertSql( - $@"SELECT `t`.`Name` -FROM ( - SELECT `l`.`Name` - FROM `Leaders` AS `l` - WHERE (`l`.`Name` LIKE 'Bran' + '%' AND (LEFT(`l`.`Name`, LEN('Bran')) = 'Bran')) AND ((`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL) -) AS `t` -WHERE (`t`.`Name` <> 'Bar') OR `t`.`Name` IS NULL"); - } + Assert.Single(query); + var result = query[0]; + Assert.Equal(2, result.Count()); } } - private JetTestStore CreateDatabase11803() + private class MyContext12582 : DbContext { - return CreateTestStore( - () => new MyContext11803(_options), - context => - { - var f1 = new Faction { Name = "Skeliege" }; - var f2 = new Faction { Name = "Monsters" }; - var f3 = new Faction { Name = "Nilfgaard" }; - var f4 = new Faction { Name = "Northern Realms" }; - var f5 = new Faction { Name = "Scioia'tael" }; - - var l11 = new Leader { Faction = f1, Name = "Bran Tuirseach" }; - var l12 = new Leader { Faction = f1, Name = "Crach an Craite" }; - var l13 = new Leader { Faction = f1, Name = "Eist Tuirseach" }; - var l14 = new Leader { Faction = f1, Name = "Harald the Cripple" }; - - context.Factions.AddRange(f1, f2, f3, f4, f5); - context.Leaders.AddRange(l11, l12, l13, l14); + public DbSet Employees { get; set; } + public DbSet Devices { get; set; } - context.SaveChanges(); + public MyContext12582(DbContextOptions options) + : base(options) + { + } - ClearLog(); - }); - } + public void Seed() + { + var d1 = new EmployeeDevice12582 { Device = "d1" }; + var d2 = new EmployeeDevice12582 { Device = "d2" }; + var e = new Employee12582 { Devices = new List { d1, d2 }, Name = "e" }; - private class MyContext11803 : DbContext - { - public DbSet Factions { get; set; } - public DbSet Leaders { get; set; } - public DbSet LeadersQuery { get; set; } + Devices.AddRange(d1, d2); + Employees.Add(e); + SaveChanges(); + } - public MyContext11803(DbContextOptions options) - : base(options) + public interface IEmployee12582 { + string Name { get; set; } } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public class Employee12582 : IEmployee12582 { - modelBuilder.Entity().HasQueryFilter(l => l.Name.StartsWith("Bran")); // this one is ignored - modelBuilder.Entity().HasQueryFilter(f => Leaders.Any(l => l.Name == "Crach an Craite")); + public int Id { get; set; } + public string Name { get; set; } + public ICollection Devices { get; set; } + } - modelBuilder - .Entity() - .HasNoKey() - .ToSqlQuery("select Name from s where Name <> 'Foo'"); + public interface IEmployeeDevice12582 + { + string Device { get; set; } + } - modelBuilder - .Entity() - .HasNoKey() - .ToSqlQuery("select 'Not Bar' as Name from Factions where Name <> 'Bar'"); + public class EmployeeDevice12582 : IEmployeeDevice12582 + { + public int Id { get; set; } + public int EmployeeId { get; set; } + public string Device { get; set; } + public Employee12582 Employee { get; set; } } } - private class Faction - { - public int Id { get; set; } - public string Name { get; set; } + #endregion - public List Leaders { get; set; } - } + #region Issue12748 - private class Leader + [ConditionalFact] + public virtual async Task Correlated_collection_correctly_associates_entities_with_byte_array_keys() { - public int Id { get; set; } - public string Name { get; set; } - public Faction Faction { get; set; } - } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - private class FactionQuery - { - public string Name { get; set; } + using (var context = contextFactory.CreateContext()) + { + var query = from blog in context.Blogs + select new + { + blog.Name, + Comments = blog.Comments.Select( + u => new { u.Id }).ToArray() + }; + var result = query.ToList(); + Assert.Single(result[0].Comments); + } } - private class LeaderQuery + protected class MyContext12748 : DbContext { - public string Name { get; set; } - } - - #endregion - - #region Bug11923 - -#pragma warning disable IDE0060 // Remove unused parameter - private static bool ClientMethod11923(int id) => true; -#pragma warning restore IDE0060 // Remove unused parameter + public DbSet Blogs { get; set; } + public DbSet Comments { get; set; } - [ConditionalFact(Skip = "Issue #17244")] - public virtual void Collection_without_setter_materialized_correctly() - { - using (CreateDatabase11923()) + public MyContext12748(DbContextOptions options) + : base(options) { - using (var context = new MyContext11923(_options)) - { - var query1 = context.Blogs - .Select( - b => new - { - Collection1 = b.Posts1, - Collection2 = b.Posts2, - Collection3 = b.Posts3 - }).ToList(); + } - var query2 = context.Blogs - .Select( - b => new - { - Collection1 = b.Posts1.OrderBy(p => p.Id).First().Comments.Count, - Collection2 = b.Posts2.OrderBy(p => p.Id).First().Comments.Count, - Collection3 = b.Posts3.OrderBy(p => p.Id).First().Comments.Count - }).ToList(); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } - var query3 = context.Blogs - .Select( - b => new - { - Collection1 = b.Posts1.OrderBy(p => p.Id), - Collection2 = b.Posts2.OrderBy(p => p.Id), - Collection3 = b.Posts3.OrderBy(p => p.Id) - }).ToList(); + public void Seed() + { + Blogs.Add(new Blog12748 { Name = Encoding.UTF8.GetBytes("Awesome Blog") }); + Comments.Add(new Comment12748 { BlogName = Encoding.UTF8.GetBytes("Awesome Blog") }); + SaveChanges(); + } - var query4 = context.Blogs - .Where(b => ClientMethod11923(b.Id)) - .Select( - b => new - { - Collection1 = b.Posts1, - Collection2 = b.Posts2, - Collection3 = b.Posts3 - }).ToList(); + public class Blog12748 + { + [Key] + public byte[] Name { get; set; } - var query5 = context.Blogs - .Where(b => ClientMethod11923(b.Id)) - .Select( - b => new - { - Collection1 = b.Posts1.OrderBy(p => p.Id).First().Comments.Count, - Collection2 = b.Posts2.OrderBy(p => p.Id).First().Comments.Count, - Collection3 = b.Posts3.OrderBy(p => p.Id).First().Comments.Count - }).ToList(); + public List Comments { get; set; } + } - var query6 = context.Blogs - .Where(b => ClientMethod11923(b.Id)) - .Select( - b => new - { - Collection1 = b.Posts1.OrderBy(p => p.Id), - Collection2 = b.Posts2.OrderBy(p => p.Id), - Collection3 = b.Posts3.OrderBy(p => p.Id) - }).ToList(); - } + public class Comment12748 + { + public int Id { get; set; } + public byte[] BlogName { get; set; } + public Blog12748 Blog { get; set; } } } - private JetTestStore CreateDatabase11923() + #endregion + + #region Issue13025 + + [ConditionalFact] + public virtual async Task Find_underlying_property_after_GroupJoin_DefaultIfEmpty() { - return CreateTestStore( - () => new MyContext11923(_options), - context => - { - var p111 = new Post11923 { Name = "P111" }; - var p112 = new Post11923 { Name = "P112" }; - var p121 = new Post11923 { Name = "P121" }; - var p122 = new Post11923 { Name = "P122" }; - var p123 = new Post11923 { Name = "P123" }; - var p131 = new Post11923 { Name = "P131" }; - - var p211 = new Post11923 { Name = "P211" }; - var p212 = new Post11923 { Name = "P212" }; - var p221 = new Post11923 { Name = "P221" }; - var p222 = new Post11923 { Name = "P222" }; - var p223 = new Post11923 { Name = "P223" }; - var p231 = new Post11923 { Name = "P231" }; - - var b1 = new Blog11923 { Name = "B1" }; - var b2 = new Blog11923 { Name = "B2" }; - - b1.Posts1.AddRange(new[] { p111, p112 }); - b1.Posts2.AddRange(new[] { p121, p122, p123 }); - b1.Posts3.Add(p131); - - b2.Posts1.AddRange(new[] { p211, p212 }); - b2.Posts2.AddRange(new[] { p221, p222, p223 }); - b2.Posts3.Add(p231); - - context.Blogs.AddRange(b1, b2); - context.Posts.AddRange(p111, p112, p121, p122, p123, p131, p211, p212, p221, p222, p223, p231); - context.SaveChanges(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - ClearLog(); - }); + using (var context = contextFactory.CreateContext()) + { + var query = (from e in context.Employees + join d in context.EmployeeDevices + on e.Id equals d.EmployeeId into grouping + from j in grouping.DefaultIfEmpty() + select new MyContext13025.Holder13025 { Name = e.Name, DeviceId = j.DeviceId }).ToList(); + } } - private class MyContext11923 : DbContext + protected class MyContext13025 : DbContext { - public DbSet Blogs { get; set; } - public DbSet Posts { get; set; } - public DbSet Comments { get; set; } + public DbSet Employees { get; set; } + public DbSet EmployeeDevices { get; set; } - public MyContext11923(DbContextOptions options) + public MyContext13025(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity( - b => + } + + public void Seed() + { + AddRange( + new Employee13025 { - b.HasMany(e => e.Posts1).WithOne().HasForeignKey("BlogId1"); - b.HasMany(e => e.Posts2).WithOne().HasForeignKey("BlogId2"); - b.HasMany(e => e.Posts3).WithOne().HasForeignKey("BlogId3"); + Name = "Test1", + Devices = new List { new() { DeviceId = 1, Device = "Battery" } } }); - modelBuilder.Entity(); + SaveChanges(); } - } - private class Blog11923 - { - public Blog11923() + public class Holder13025 { - Posts1 = new List(); - Posts2 = new CustomCollection11923(); - Posts3 = new HashSet(); + public string Name { get; set; } + public int? DeviceId { get; set; } } - public Blog11923(List posts1, CustomCollection11923 posts2, HashSet posts3) + public class Employee13025 { - Posts1 = posts1; - Posts2 = posts2; - Posts3 = posts3; + public int Id { get; set; } + public string Name { get; set; } + public ICollection Devices { get; set; } } - public int Id { get; set; } - public string Name { get; set; } - - public List Posts1 { get; } - public CustomCollection11923 Posts2 { get; } - public HashSet Posts3 { get; } - } - - private class Post11923 - { - public int Id { get; set; } - public string Name { get; set; } - - public List Comments { get; set; } - } - - private class Comment11923 - { - public int Id { get; set; } - } - - private class CustomCollection11923 : List - { + public class EmployeeDevice13025 + { + public int Id { get; set; } + public short DeviceId { get; set; } + public int EmployeeId { get; set; } + public string Device { get; set; } + public Employee13025 Employee { get; set; } + } } #endregion - #region Bug11885 + #region Issue12170 [ConditionalFact] - public virtual void Average_with_cast() + public virtual async Task Weak_entities_with_query_filter_subquery_flattening() { - using (CreateDatabase11885()) - { - using (var context = new MyContext11885(_options)) - { - var prices = context.Prices.ToList(); - - ClearLog(); + var contextFactory = await InitializeAsync(); - Assert.Equal(prices.Average(e => e.Price), context.Prices.Average(e => e.Price)); - Assert.Equal(prices.Average(e => e.IntColumn), context.Prices.Average(e => e.IntColumn)); - Assert.Equal(prices.Average(e => e.NullableIntColumn), context.Prices.Average(e => e.NullableIntColumn)); - Assert.Equal(prices.Average(e => e.LongColumn), context.Prices.Average(e => e.LongColumn)); - Assert.Equal(prices.Average(e => e.NullableLongColumn), context.Prices.Average(e => e.NullableLongColumn)); - Assert.Equal(prices.Average(e => e.FloatColumn), context.Prices.Average(e => e.FloatColumn)); - Assert.Equal(prices.Average(e => e.NullableFloatColumn), context.Prices.Average(e => e.NullableFloatColumn)); - Assert.Equal(prices.Average(e => e.DoubleColumn), context.Prices.Average(e => e.DoubleColumn)); - Assert.Equal(prices.Average(e => e.NullableDoubleColumn), context.Prices.Average(e => e.NullableDoubleColumn)); - Assert.Equal(prices.Average(e => e.DecimalColumn), context.Prices.Average(e => e.DecimalColumn)); - Assert.Equal(prices.Average(e => e.NullableDecimalColumn), context.Prices.Average(e => e.NullableDecimalColumn)); + using (var context = contextFactory.CreateContext()) + { + var result = context.Definitions.Any(); - AssertSql( - $@"SELECT AVG(`p`.`Price`) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(CDBL(`p`.`IntColumn`)) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(IIF(`p`.`NullableIntColumn` IS NULL, NULL, CDBL(`p`.`NullableIntColumn`))) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(CDBL(`p`.`LongColumn`)) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(IIF(`p`.`NullableLongColumn` IS NULL, NULL, CDBL(`p`.`NullableLongColumn`))) -FROM `Prices` AS `p`", - // - $@"SELECT CSNG(AVG(`p`.`FloatColumn`)) -FROM `Prices` AS `p`", - // - $@"SELECT CSNG(AVG(`p`.`NullableFloatColumn`)) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(`p`.`DoubleColumn`) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(`p`.`NullableDoubleColumn`) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(`p`.`DecimalColumn`) -FROM `Prices` AS `p`", - // - $@"SELECT AVG(`p`.`NullableDecimalColumn`) -FROM `Prices` AS `p`"); - } + Assert.False(result); } } - private JetTestStore CreateDatabase11885() + protected class MyContext12170 : DbContext { - return CreateTestStore( - () => new MyContext11885(_options), - context => - { - context.AddRange( - new Price11885 - { - IntColumn = 1, - NullableIntColumn = 1, - LongColumn = 1000, - NullableLongColumn = 1000, - FloatColumn = 0.1F, - NullableFloatColumn = 0.1F, - DoubleColumn = 0.000001, - NullableDoubleColumn = 0.000001, - DecimalColumn = 1.0m, - NullableDecimalColumn = 1.0m, - Price = 0.00112000m - }, - new Price11885 - { - IntColumn = 2, - NullableIntColumn = 2, - LongColumn = 2000, - NullableLongColumn = 2000, - FloatColumn = 0.2F, - NullableFloatColumn = 0.2F, - DoubleColumn = 0.000002, - NullableDoubleColumn = 0.000002, - DecimalColumn = 2.0m, - NullableDecimalColumn = 2.0m, - Price = 0.00232111m - }, - new Price11885 - { - IntColumn = 3, - LongColumn = 3000, - FloatColumn = 0.3F, - DoubleColumn = 0.000003, - DecimalColumn = 3.0m, - Price = 0.00345223m - } - ); - - context.SaveChanges(); + public virtual DbSet Definitions { get; set; } + public virtual DbSet DefinitionHistories { get; set; } - ClearLog(); - }); - } - - private class MyContext11885 : DbContext - { - public DbSet Prices { get; set; } - - public MyContext11885(DbContextOptions options) + public MyContext12170(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity( - b => - { - b.Property(e => e.Price).HasColumnType("DECIMAL(18, 8)"); - b.Property(e => e.DecimalColumn).HasColumnType("DECIMAL(18, 2)"); - b.Property(e => e.NullableDecimalColumn).HasColumnType("DECIMAL(18, 2)"); - }); - } - } + modelBuilder.Entity().HasQueryFilter(md => md.ChangeInfo.RemovedPoint.Timestamp == null); + modelBuilder.Entity().HasOne(h => h.LatestHistoryEntry).WithMany(); + modelBuilder.Entity().HasMany(h => h.HistoryEntries).WithOne(h => h.Definition); - private class Price11885 - { - public int Id { get; set; } - public int IntColumn { get; set; } - public int? NullableIntColumn { get; set; } - public long LongColumn { get; set; } - public long? NullableLongColumn { get; set; } - public float FloatColumn { get; set; } - public float? NullableFloatColumn { get; set; } - public double DoubleColumn { get; set; } - public double? NullableDoubleColumn { get; set; } - public decimal DecimalColumn { get; set; } - public decimal? NullableDecimalColumn { get; set; } - public decimal Price { get; set; } - } + modelBuilder.Entity().OwnsOne(h => h.EndedPoint); + } - #endregion + [Owned] + public class OptionalChangePoint12170 + { + public int Value { get; set; } + public DateTime? Timestamp { get; set; } + } - #region Bug12582 + [Owned] + public class MasterChangeInfo12170 + { + public bool Exists { get; set; } + public virtual OptionalChangePoint12170 RemovedPoint { get; set; } + } - [ConditionalFact(Skip = "Issue #17244")] - public virtual void Include_collection_with_OfType_base() - { - using (CreateDatabase12582()) + public class DefinitionHistory12170 { - using (var context = new MyContext12582(_options)) - { - var query = context.Employees - .Include(i => i.Devices) - .OfType() - .ToList(); + public int Id { get; set; } + public int MacGuffinDefinitionID { get; set; } + public virtual Definition12170 Definition { get; set; } + public OptionalChangePoint12170 EndedPoint { get; set; } + } - Assert.Single(query); + public class Definition12170 + { + public int Id { get; set; } + public virtual MasterChangeInfo12170 ChangeInfo { get; set; } - var employee = (Employee12582)query[0]; - Assert.Equal(2, employee.Devices.Count); - } + public virtual ICollection HistoryEntries { get; set; } + public virtual DefinitionHistory12170 LatestHistoryEntry { get; set; } + public int? LatestHistoryEntryID { get; set; } } } + #endregion + + #region Issue11944 + [ConditionalFact] - public virtual void Correlated_collection_with_OfType_base() + public virtual async Task Include_collection_works_when_defined_on_intermediate_type() { - using (CreateDatabase12582()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext12582(_options)) - { - var query = context.Employees - .Select(e => e.Devices.Where(d => d.Device != "foo").Cast()) - .ToList(); + var query = context.Schools.Include(s => ((MyContext11944.ElementarySchool11944)s).Students); + var result = query.ToList(); - Assert.Single(query); - var result = query[0]; - Assert.Equal(2, result.Count()); - } + Assert.Equal(2, result.Count); + Assert.Equal(2, result.OfType().Single().Students.Count); + } + + using (var context = contextFactory.CreateContext()) + { + var query = context.Schools.Select(s => ((MyContext11944.ElementarySchool11944)s).Students.Where(ss => true).ToList()); + var result = query.ToList(); + + Assert.Equal(2, result.Count); + Assert.Contains(result, r => r.Count() == 2); } } - private JetTestStore CreateDatabase12582() + protected class MyContext11944 : DbContext { - return CreateTestStore( - () => new MyContext12582(_options), - context => - { - var d1 = new EmployeeDevice12582 { Device = "d1" }; - var d2 = new EmployeeDevice12582 { Device = "d2" }; - var e = new Employee12582 { Devices = new List { d1, d2 }, Name = "e" }; + public DbSet Students { get; set; } + public DbSet Schools { get; set; } + public DbSet ElementarySchools { get; set; } - context.Devices.AddRange(d1, d2); - context.Employees.Add(e); - context.SaveChanges(); + public MyContext11944(DbContextOptions options) + : base(options) + { + } - ClearLog(); - }); - } + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasMany(s => s.Students).WithOne(s => s.School); - private interface IEmployee12582 - { - string Name { get; set; } - } + public void Seed() + { + var student1 = new Student11944(); + var student2 = new Student11944(); + var school = new School11944(); + var elementarySchool = new ElementarySchool11944 { Students = new List { student1, student2 } }; - private class Employee12582 : IEmployee12582 - { - public int Id { get; set; } - public string Name { get; set; } - public ICollection Devices { get; set; } - } + Students.AddRange(student1, student2); + Schools.AddRange(school); + ElementarySchools.Add(elementarySchool); - private interface IEmployeeDevice12582 - { - string Device { get; set; } - } + SaveChanges(); + } - private class EmployeeDevice12582 : IEmployeeDevice12582 - { - public int Id { get; set; } - public int EmployeeId { get; set; } - public string Device { get; set; } - public Employee12582 Employee { get; set; } - } + public class Student11944 + { + public int Id { get; set; } + public ElementarySchool11944 School { get; set; } + } - private class MyContext12582 : DbContext - { - public DbSet Employees { get; set; } - public DbSet Devices { get; set; } + public class School11944 + { + public int Id { get; set; } + } - public MyContext12582(DbContextOptions options) - : base(options) + public abstract class PrimarySchool11944 : School11944 + { + public List Students { get; set; } + } + + public class ElementarySchool11944 : PrimarySchool11944 { } } #endregion - #region Bug12748 + #region Issue13118 [ConditionalFact] - public virtual void Correlated_collection_correctly_associates_entities_with_byte_array_keys() + public virtual async Task DateTime_Contains_with_smalldatetime_generates_correct_literal() { - using (CreateDatabase12748()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext12748(_options)) - { - var query = from blog in context.Blogs - select new - { - blog.Name, - Comments = blog.Comments.Select( - u => new { u.Id }).ToArray() - }; - var result = query.ToList(); - Assert.Single(result[0].Comments); - } - } - } + var testDateList = new List { new(2018, 10, 07) }; + var findRecordsWithDateInList = context.ReproEntity + .Where(a => testDateList.Contains(a.MyTime)) + .ToList(); - private JetTestStore CreateDatabase12748() - { - return CreateTestStore( - () => new MyContext12748(_options), - context => - { - context.Blogs.Add(new Blog12748 { Name = Encoding.UTF8.GetBytes("Awesome Blog") }); - context.Comments.Add(new Comment12748 { BlogName = Encoding.UTF8.GetBytes("Awesome Blog") }); - context.SaveChanges(); - ClearLog(); - }); + Assert.Single(findRecordsWithDateInList); + + AssertSql( +""" +SELECT `r`.`Id`, `r`.`MyTime` +FROM `ReproEntity` AS `r` +WHERE `r`.`MyTime` = #2018-10-07# +"""); + } } - private class MyContext12748 : DbContext + private class MyContext13118 : DbContext { - public DbSet Blogs { get; set; } - public DbSet Comments { get; set; } + public virtual DbSet ReproEntity { get; set; } - public MyContext12748(DbContextOptions options) + public MyContext13118(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) - { - } - } + => modelBuilder.Entity(e => e.Property("MyTime").HasColumnType("smalldatetime")); - private class Blog12748 - { - [Key] - public byte[] Name { get; set; } + public void Seed() + { + AddRange( + new ReproEntity13118 { MyTime = new DateTime(2018, 10, 07) }, + new ReproEntity13118 { MyTime = new DateTime(2018, 10, 08) }); - public List Comments { get; set; } + SaveChanges(); + } } - private class Comment12748 + private class ReproEntity13118 { - public int Id { get; set; } - public byte[] BlogName { get; set; } - public Blog12748 Blog { get; set; } + public Guid Id { get; set; } + public DateTime MyTime { get; set; } } #endregion - #region Bug13025 + #region Issue12732 [ConditionalFact] - public virtual void Find_underlying_property_after_GroupJoin_DefaultIfEmpty() + public virtual async Task Nested_contains_with_enum() { - using (CreateDatabase13025()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext13025(_options)) - { - var query = (from e in context.Employees - join d in context.EmployeeDevices - on e.Id equals d.EmployeeId into grouping - from j in grouping.DefaultIfEmpty() - select new Holder13025 { Name = e.Name, DeviceId = j.DeviceId }).ToList(); - } - } - } + var key = Guid.Parse("5f221fb9-66f4-442a-92c9-d97ed5989cc7"); + var keys = new List { Guid.Parse("0a47bcb7-a1cb-4345-8944-c58f82d6aac7"), key }; + var todoTypes = new List { MyContext12732.TodoType.foo0 }; - private class Holder13025 - { - public string Name { get; set; } - public int? DeviceId { get; set; } - } + // Note that in this query, the outer Contains really has no type mapping, neither for its source (collection parameter), nor + // for its item (the conditional expression returns key, which is also a parameter). The default type mapping must be applied. + var query = context.Todos + .Where(x => keys.Contains(todoTypes.Contains(x.Type) ? key : key)) + .ToList(); - private JetTestStore CreateDatabase13025() - { - return CreateTestStore( - () => new MyContext13025(_options), - context => - { - context.AddRange( - new Employee13025 - { - Name = "Test1", - Devices = new List { new EmployeeDevice13025 { DeviceId = 1, Device = "Battery" } } - }); + Assert.Single(query); - context.SaveChanges(); - ClearLog(); - }); + AssertSql( + """ +@__key_2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' +@__keys_0='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) + +SELECT [t].[Id], [t].[Type] +FROM [Todos] AS [t] +WHERE CASE + WHEN [t].[Type] = 0 THEN @__key_2 + ELSE @__key_2 +END IN ( + SELECT [k].[value] + FROM OPENJSON(@__keys_0) WITH ([value] uniqueidentifier '$') AS [k] +) +"""); + } } - private class MyContext13025 : DbContext + protected class MyContext12732 : DbContext { - public DbSet Employees { get; set; } - public DbSet EmployeeDevices { get; set; } + public DbSet Todos { get; set; } - public MyContext13025(DbContextOptions options) + public MyContext12732(DbContextOptions options) : base(options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public void Seed() { + Add(new Todo { Type = TodoType.foo0 }); + SaveChanges(); } - } - private class Employee13025 - { - public int Id { get; set; } - public string Name { get; set; } - public ICollection Devices { get; set; } - } + public class Todo + { + public Guid Id { get; set; } + public TodoType Type { get; set; } + } - private class EmployeeDevice13025 - { - public int Id { get; set; } - public short DeviceId { get; set; } - public int EmployeeId { get; set; } - public string Device { get; set; } - public Employee13025 Employee { get; set; } + public enum TodoType + { + foo0 = 0 + } } #endregion - #region Bug12170 + #region Issue13157 [ConditionalFact] - public virtual void Weak_entities_with_query_filter_subquery_flattening() + public virtual async Task Correlated_subquery_with_owned_navigation_being_compared_to_null_works() { - using (CreateDatabase12170()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext12170(_options)) - { - var result = context.Definitions.Any(); - } - } - } + var partners = context.Partners + .Select( + x => new + { + Addresses = x.Addresses.Select( + y => new + { + Turnovers = y.Turnovers == null + ? null + : new { y.Turnovers.AmountIn } + }).ToList() + }).ToList(); - private JetTestStore CreateDatabase12170() - { - return CreateTestStore( - () => new MyContext12170(_options), - context => - { - context.SaveChanges(); - ClearLog(); - }); + Assert.Single(partners); + Assert.Collection( + partners[0].Addresses, + t => + { + Assert.NotNull(t.Turnovers); + Assert.Equal(10, t.Turnovers.AmountIn); + }, + t => + { + Assert.Null(t.Turnovers); + }); + + AssertSql( + """ +SELECT [p].[Id], CASE + WHEN [a].[Turnovers_AmountIn] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END, [a].[Turnovers_AmountIn], [a].[Id] +FROM [Partners] AS [p] +LEFT JOIN [Address13157] AS [a] ON [p].[Id] = [a].[Partner13157Id] +ORDER BY [p].[Id] +"""); + } } - private class MyContext12170 : DbContext + protected class MyContext13157 : DbContext { - public virtual DbSet Definitions { get; set; } - public virtual DbSet DefinitionHistories { get; set; } + public virtual DbSet Partners { get; set; } - public MyContext12170(DbContextOptions options) + public MyContext13157(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().OwnsOne(x => x.Turnovers); + + public void Seed() { - modelBuilder.Entity().HasQueryFilter(md => md.ChangeInfo.RemovedPoint.Timestamp == null); - modelBuilder.Entity().HasOne(h => h.LatestHistoryEntry).WithMany(); - modelBuilder.Entity().HasMany(h => h.HistoryEntries).WithOne(h => h.Definition); + AddRange( + new Partner13157 + { + Addresses = new List + { + new() { Turnovers = new AddressTurnovers13157 { AmountIn = 10 } }, new() { Turnovers = null }, + } + } + ); - modelBuilder.Entity().OwnsOne(h => h.EndedPoint); + SaveChanges(); } - } - - [Owned] - private class OptionalChangePoint12170 - { - public DateTime? Timestamp { get; set; } - } - [Owned] - private class MasterChangeInfo12170 - { - public virtual OptionalChangePoint12170 RemovedPoint { get; set; } - } - - private class DefinitionHistory12170 - { - public int Id { get; set; } - public int MacGuffinDefinitionID { get; set; } - public virtual Definition12170 Definition { get; set; } - public OptionalChangePoint12170 EndedPoint { get; set; } - } + public class Partner13157 + { + public int Id { get; set; } + public ICollection Addresses { get; set; } + } - private class Definition12170 - { - public int Id { get; set; } - public virtual MasterChangeInfo12170 ChangeInfo { get; set; } + public class Address13157 + { + public int Id { get; set; } + public AddressTurnovers13157 Turnovers { get; set; } + } - public virtual ICollection HistoryEntries { get; set; } - public virtual DefinitionHistory12170 LatestHistoryEntry { get; set; } - public int? LatestHistoryEntryID { get; set; } + public class AddressTurnovers13157 + { + public int AmountIn { get; set; } + } } #endregion - #region Bug11944 + #region Issue13079 [ConditionalFact] - public virtual void Include_collection_works_when_defined_on_intermediate_type() + public virtual async Task Multilevel_owned_entities_determine_correct_nullability() { - using (CreateDatabase11944()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext11944(_options)) - { - var query = context.Schools.Include(s => ((ElementarySchool11944)s).Students); - var result = query.ToList(); + await context.AddAsync(new MyContext13079.BaseEntity13079()); + context.SaveChanges(); - Assert.Equal(2, result.Count); - Assert.Equal(2, result.OfType().Single().Students.Count); - } + AssertSql( +""" +@p0='BaseEntity13079' (Nullable = false) (Size = 21) + +INSERT INTO `BaseEntities` (`Discriminator`) +VALUES (@p0); +SELECT `Id` +FROM `BaseEntities` +WHERE @@ROWCOUNT = 1 AND `Id` = @@identity; +"""); } } - [ConditionalFact] - public virtual void Correlated_collection_works_when_defined_on_intermediate_type() + protected class MyContext13079 : DbContext { - using (CreateDatabase11944()) + public virtual DbSet BaseEntities { get; set; } + + public MyContext13079(DbContextOptions options) + : base(options) { - using (var context = new MyContext11944(_options)) - { - var query = context.Schools.Select(s => ((ElementarySchool11944)s).Students.Where(ss => true).ToList()); - var result = query.ToList(); + } - Assert.Equal(2, result.Count); - Assert.Contains(result, r => r.Count() == 2); - } + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().OwnsOne(e => e.Data, b => b.OwnsOne(e => e.SubData)); + + public class BaseEntity13079 + { + public int Id { get; set; } + } + + public class DerivedEntity13079 : BaseEntity13079 + { + public int Property { get; set; } + public OwnedData13079 Data { get; set; } + } + + public class OwnedData13079 + { + public int Property { get; set; } + public OwnedSubData13079 SubData { get; set; } + } + + public class OwnedSubData13079 + { + public int Property { get; set; } } } - private JetTestStore CreateDatabase11944() + #endregion + + #region Issue13587 + + [ConditionalFact] + public virtual async Task Type_casting_inside_sum() { - return CreateTestStore( - () => new MyContext11944(_options), - context => - { - var student1 = new Student11944(); - var student2 = new Student11944(); - var school = new School11944(); - var elementarySchool = new ElementarySchool11944 { Students = new List { student1, student2 } }; + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - context.Students.AddRange(student1, student2); - context.Schools.AddRange(school); - context.ElementarySchools.Add(elementarySchool); + using (var context = contextFactory.CreateContext()) + { + var result = context.InventoryPools.Sum(p => (decimal)p.Quantity); - context.SaveChanges(); - ClearLog(); - }); + AssertSql( + """ +SELECT COALESCE(SUM(CAST([i].[Quantity] AS decimal(18,2))), 0.0) +FROM [InventoryPools] AS [i] +"""); + } } - private class MyContext11944 : DbContext + protected class MyContext13587 : DbContext { - public DbSet Students { get; set; } - public DbSet Schools { get; set; } - public DbSet ElementarySchools { get; set; } + public virtual DbSet InventoryPools { get; set; } - public MyContext11944(DbContextOptions options) + public MyContext13587(DbContextOptions options) : base(options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public void Seed() { - modelBuilder.Entity().HasMany(s => s.Students).WithOne(s => s.School); + InventoryPools.Add(new InventoryPool13587 { Quantity = 2 }); + + SaveChanges(); } - } - - private class Student11944 - { - public int Id { get; set; } - public ElementarySchool11944 School { get; set; } - } - - private class School11944 - { - public int Id { get; set; } - } - - private abstract class PrimarySchool11944 : School11944 - { - public List Students { get; set; } - } - private class ElementarySchool11944 : PrimarySchool11944 - { + public class InventoryPool13587 + { + public int Id { get; set; } + public double Quantity { get; set; } + } } #endregion - #region Bug13118 + #region Issue12518 [ConditionalFact] - public virtual void DateTime_Contains_with_smalldatetime_generates_correct_literal() + public virtual async Task Projecting_entity_with_value_converter_and_include_works() { - using (CreateDatabase13118()) - { - using (var context = new MyContext13118(_options)) - { - var testDateList = new List { new DateTime(2018, 10, 07) }; - var findRecordsWithDateInList = context.ReproEntity - .Where(a => testDateList.Contains(a.MyTime)) - .ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - Assert.Single(findRecordsWithDateInList); + using (var context = contextFactory.CreateContext()) + { + var result = context.Parents.Include(p => p.Child).OrderBy(e => e.Id).FirstOrDefault(); - AssertSql( - $@"SELECT `r`.`Id`, `r`.`MyTime` -FROM `ReproEntity` AS `r` -WHERE `r`.`MyTime` = #2018-10-07#"); - } + AssertSql( + """ +SELECT TOP(1) [p].[Id], [p].[ChildId], [c].[Id], [c].[ParentId], [c].[ULongRowVersion] +FROM [Parents] AS [p] +LEFT JOIN [Children] AS [c] ON [p].[ChildId] = [c].[Id] +ORDER BY [p].[Id] +"""); } } - private JetTestStore CreateDatabase13118() + [ConditionalFact] + public virtual async Task Projecting_column_with_value_converter_of_ulong_byte_array() { - return CreateTestStore( - () => new MyContext13118(_options), - context => - { - context.AddRange( - new ReproEntity13118 { MyTime = new DateTime(2018, 10, 07) }, - new ReproEntity13118 { MyTime = new DateTime(2018, 10, 08) }); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - context.SaveChanges(); - ClearLog(); - }); + using (var context = contextFactory.CreateContext()) + { + var result = context.Parents.OrderBy(e => e.Id).Select(p => (ulong?)p.Child.ULongRowVersion).FirstOrDefault(); + + AssertSql( + """ +SELECT TOP(1) [c].[ULongRowVersion] +FROM [Parents] AS [p] +LEFT JOIN [Children] AS [c] ON [p].[ChildId] = [c].[Id] +ORDER BY [p].[Id] +"""); + } } - private class MyContext13118 : DbContext + protected class MyContext12518 : DbContext { - public virtual DbSet ReproEntity { get; set; } + public virtual DbSet Parents { get; set; } + public virtual DbSet Children { get; set; } - public MyContext13118(DbContextOptions options) + public MyContext12518(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity(e => e.Property("MyTime").HasColumnType("smalldatetime")); + var child = modelBuilder.Entity(); + child.HasOne(_ => _.Parent) + .WithOne(_ => _.Child) + .HasForeignKey(_ => _.ChildId); + child.Property(x => x.ULongRowVersion) + .HasConversion(new NumberToBytesConverter()) + .IsRowVersion() + .IsRequired() + .HasColumnType("RowVersion"); + + modelBuilder.Entity(); + } + + public void Seed() + { + Parents.Add(new Parent12518()); + SaveChanges(); } - } - private class ReproEntity13118 - { - public Guid Id { get; set; } - public DateTime MyTime { get; set; } + public class Parent12518 + { + public Guid Id { get; set; } = Guid.NewGuid(); + public Guid? ChildId { get; set; } + public Child12518 Child { get; set; } + } + + public class Child12518 + { + public Guid Id { get; set; } = Guid.NewGuid(); + public ulong ULongRowVersion { get; set; } + public Guid ParentId { get; set; } + public Parent12518 Parent { get; set; } + } } #endregion - #region Bug12732 + #region Issue12549 [ConditionalFact] - public virtual void Nested_contains_with_enum() + public virtual async Task Union_and_insert_12549() { - using (CreateDatabase12732()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext12732(_options)) - { - var key = Guid.Parse("5f221fb9-66f4-442a-92c9-d97ed5989cc7"); - var keys = new List { Guid.Parse("0a47bcb7-a1cb-4345-8944-c58f82d6aac7"), key }; - var todoTypes = new List { TodoType.foo0 }; + var id1 = 1; + var id2 = 2; - var query = context.Todos - .Where(x => keys.Contains(todoTypes.Contains(x.Type) ? key : key)) - .ToList(); + var ids1 = context.Set() + .Where(x => x.Id == id1) + .Select(x => x.Id); - Assert.Single(query); + var ids2 = context.Set() + .Where(x => x.Id == id2) + .Select(x => x.Id); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__key_2='5f221fb9-66f4-442a-92c9-d97ed5989cc7'")} + var results = ids1.Union(ids2).ToList(); -SELECT `t`.`Id`, `t`.`Type` -FROM `Todos` AS `t` -WHERE CASE - WHEN `t`.`Type` IN (0) THEN {AssertSqlHelper.Parameter("@__key_2")} - ELSE {AssertSqlHelper.Parameter("@__key_2")} -END IN ('0a47bcb7-a1cb-4345-8944-c58f82d6aac7', '5f221fb9-66f4-442a-92c9-d97ed5989cc7')"); - } + context.AddRange( + new MyContext12549.Table1_12549(), + new MyContext12549.Table2_12549(), + new MyContext12549.Table1_12549(), + new MyContext12549.Table2_12549()); + context.SaveChanges(); } } - private JetTestStore CreateDatabase12732() - { - return CreateTestStore( - () => new MyContext12732(_options), - context => - { - context.Add( - new Todo { Type = TodoType.foo0 }); - context.SaveChanges(); - ClearLog(); - }); - } - - private class MyContext12732 : DbContext + private class MyContext12549 : DbContext { - public DbSet Todos { get; set; } + public DbSet Table1 { get; set; } + public DbSet Table2 { get; set; } - public MyContext12732(DbContextOptions options) + public MyContext12549(DbContextOptions options) : base(options) { } - } - private class Todo - { - public Guid Id { get; set; } - public TodoType Type { get; set; } - } + public class Table1_12549 + { + public int Id { get; set; } + } - private enum TodoType - { - foo0 = 0 + public class Table2_12549 + { + public int Id { get; set; } + } } #endregion - #region Bug13157 + #region Issue16233 [ConditionalFact] - public virtual void Correlated_subquery_with_owned_navigation_being_compared_to_null_works() + public virtual async Task Derived_reference_is_skipped_when_base_type() { - using (CreateDatabase13157()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext13157(_options)) - { - var partners = context.Partners - .Select( - x => new - { - Addresses = x.Addresses.Select( - y => new - { - Turnovers = y.Turnovers == null - ? null - : new { y.Turnovers.AmountIn } - }).ToList() - }).ToList(); + var result = context.Bases.Include(p => ((MyContext16233.DerivedType16233)p).Reference).OrderBy(b => b.Id).ToList(); - Assert.Single(partners); - Assert.Single(partners[0].Addresses); - Assert.NotNull(partners[0].Addresses[0].Turnovers); - Assert.Equal(10, partners[0].Addresses[0].Turnovers.AmountIn); + Assert.Equal(3, result.Count); + Assert.NotNull(Assert.IsType(result[1]).Reference); + Assert.Null(Assert.IsType(result[2]).Reference); + Assert.True(context.Entry(Assert.IsType(result[2])).Reference("Reference").IsLoaded); - AssertSql( - $@"SELECT `p`.`Id`, `t0`.`c`, `t0`.`Turnovers_AmountIn`, `t0`.`Id` -FROM `Partners` AS `p` -LEFT JOIN ( - SELECT IIF(`t`.`Id` IS NULL, 1, 0) AS `c`, `t`.`Turnovers_AmountIn`, `a`.`Id`, `a`.`Partner13157Id` - FROM `Address13157` AS `a` - LEFT JOIN ( - SELECT `a0`.`Id`, `a0`.`Turnovers_AmountIn`, `a1`.`Id` AS `Id0` - FROM `Address13157` AS `a0` - INNER JOIN `Address13157` AS `a1` ON `a0`.`Id` = `a1`.`Id` - WHERE `a0`.`Turnovers_AmountIn` IS NOT NULL - ) AS `t` ON `a`.`Id` = `t`.`Id` -) AS `t0` ON `p`.`Id` = `t0`.`Partner13157Id` -ORDER BY `p`.`Id`, `t0`.`Id`"); - } + AssertSql( +""" +SELECT `b`.`Id`, `b`.`Discriminator`, `r`.`Id`, `r`.`DerivedTypeId` +FROM `Bases` AS `b` +LEFT JOIN `Reference16233` AS `r` ON `b`.`Id` = `r`.`DerivedTypeId` +ORDER BY `b`.`Id` +"""); } - } - private JetTestStore CreateDatabase13157() - { - return CreateTestStore( - () => new MyContext13157(_options), - context => - { - context.AddRange( - new Partner13157 - { - Addresses = new List - { - new Address13157 { Turnovers = new AddressTurnovers13157 { AmountIn = 10 } } - } - } - ); + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var result = context.Bases.AsNoTracking().Include(p => ((MyContext16233.DerivedType16233)p).Reference).OrderBy(b => b.Id) + .ToList(); - context.SaveChanges(); - ClearLog(); - }); + Assert.Equal(3, result.Count); + Assert.NotNull(Assert.IsType(result[1]).Reference); + Assert.NotNull(Assert.IsType(result[1]).Reference.DerivedType); + Assert.Null(Assert.IsType(result[2]).Reference); + + AssertSql( +""" +SELECT `b`.`Id`, `b`.`Discriminator`, `r`.`Id`, `r`.`DerivedTypeId` +FROM `Bases` AS `b` +LEFT JOIN `Reference16233` AS `r` ON `b`.`Id` = `r`.`DerivedTypeId` +ORDER BY `b`.`Id` +"""); + } } - private class MyContext13157 : DbContext + private class MyContext16233 : DbContext { - public virtual DbSet Partners { get; set; } + public virtual DbSet Bases { get; set; } + public virtual DbSet Derived { get; set; } - public MyContext13157(DbContextOptions options) + public MyContext16233(DbContextOptions options) : base(options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public void Seed() { - modelBuilder.Entity().OwnsOne(x => x.Turnovers); + AddRange( + new BaseType16233(), + new DerivedType16233 { Reference = new Reference16233() }, + new DerivedType16233()); + + SaveChanges(); } - } - private class Partner13157 - { - public int Id { get; set; } - public ICollection Addresses { get; set; } - } + public class BaseType16233 + { + public int Id { get; set; } + } - private class Address13157 - { - public int Id { get; set; } - public AddressTurnovers13157 Turnovers { get; set; } - } + public class DerivedType16233 : BaseType16233 + { + public Reference16233 Reference { get; set; } + } - private class AddressTurnovers13157 - { - public int AmountIn { get; set; } + public class Reference16233 + { + public int Id { get; set; } + public int DerivedTypeId { get; set; } + public DerivedType16233 DerivedType { get; set; } + } } #endregion - #region Bug13346 + #region Issue15684 [ConditionalFact] - public virtual void ToQuery_can_define_in_own_terms_using_FromSql() + public virtual async Task Projection_failing_with_EnumToStringConverter() { - using (CreateDatabase13346()) - { - using (var context = new MyContext13346(_options)) - { - var query = context.Set().ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - Assert.Equal(4, query.Count); + using (var context = contextFactory.CreateContext()) + { + var query = from p in context.Products + join c in context.Categories on p.CategoryId equals c.Id into grouping + from c in grouping.DefaultIfEmpty() + select new MyContext15684.ProductDto15684 + { + Id = p.Id, + Name = p.Name, + CategoryName = c == null ? "Other" : c.Name, + CategoryStatus = c == null ? MyContext15684.CategoryStatus15684.Active : c.Status + }; + var result = query.ToList(); + Assert.Equal(2, result.Count); - AssertSql( - $@"SELECT o.Amount From Orders AS o"); - } + AssertSql( +""" +SELECT `p`.`Id`, `p`.`Name`, IIF(`c`.`Id` IS NULL, 'Other', `c`.`Name`) AS `CategoryName`, IIF(`c`.`Id` IS NULL, 'Active', `c`.`Status`) AS `CategoryStatus` +FROM `Products` AS `p` +LEFT JOIN `Categories` AS `c` ON `p`.`CategoryId` = `c`.`Id` +"""); } } - private JetTestStore CreateDatabase13346() + protected class MyContext15684 : DbContext { - return CreateTestStore( - () => new MyContext13346(_options), - context => - { - context.AddRange( - new Order13346 { Amount = 1 }, - new Order13346 { Amount = 2 }, - new Order13346 { Amount = 3 }, - new Order13346 { Amount = 4 } - ); - - context.SaveChanges(); - ClearLog(); - }); - } - - private class MyContext13346 : DbContext - { - public virtual DbSet Orders { get; set; } + public DbSet Categories { get; set; } + public DbSet Products { get; set; } - public MyContext13346(DbContextOptions options) + public MyContext15684(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder + .Entity() + .Property(e => e.Status) + .HasConversion(new EnumToStringConverter()); + + public void Seed() { - modelBuilder.Entity() - .HasNoKey() - .ToSqlQuery("SELECT o.Amount From Orders AS o"); + Products.Add( + new Product15684 { Name = "Apple", Category = new Category15684 { Name = "Fruit", Status = CategoryStatus15684.Active } }); + + Products.Add(new Product15684 { Name = "Bike" }); + + SaveChanges(); } - } - private class Order13346 - { - public int Id { get; set; } - public int Amount { get; set; } - } + public class Product15684 + { + [Key] + public int Id { get; set; } - private class OrderSummary13346 - { - public int Amount { get; set; } - } + [Required] + public string Name { get; set; } - #endregion + public int? CategoryId { get; set; } - #region Bug13079 + public Category15684 Category { get; set; } + } - [ConditionalFact] - public virtual void Multilevel_owned_entities_determine_correct_nullability() - { - using (CreateDatabase13079()) + public class Category15684 { - using (var context = new MyContext13079(_options)) - { - context.Add(new BaseEntity13079()); - context.SaveChanges(); + [Key] + public int Id { get; set; } - AssertSql( - $@"{AssertSqlHelper.Declaration("@p0='BaseEntity13079' (Nullable = false) (Size = 255)")} + [Required] + public string Name { get; set; } -SET NOCOUNT ON; -INSERT INTO `BaseEntities` (`Discriminator`) -VALUES ({AssertSqlHelper.Parameter("@p0")}); -SELECT `Id` -FROM `BaseEntities` -WHERE @@ROWCOUNT = 1 AND `Id` = scope_identity();"); - } + public CategoryStatus15684 Status { get; set; } } - } - - private JetTestStore CreateDatabase13079() - { - return CreateTestStore( - () => new MyContext13079(_options), - context => ClearLog()); - } - - private class MyContext13079 : DbContext - { - public virtual DbSet BaseEntities { get; set; } - public MyContext13079(DbContextOptions options) - : base(options) + public class ProductDto15684 { + public string CategoryName { get; set; } + public CategoryStatus15684 CategoryStatus { get; set; } + public int Id { get; set; } + public string Name { get; set; } } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public enum CategoryStatus15684 { - modelBuilder.Entity().OwnsOne(e => e.Data, b => b.OwnsOne(e => e.SubData)); + Active = 0, + Removed = 1 } } - private class BaseEntity13079 - { - public int Id { get; set; } - } - - private class DerivedEntity13079 : BaseEntity13079 - { - public int Property { get; set; } - public OwnedData13079 Data { get; set; } - } - - private class OwnedData13079 - { - public int Property { get; set; } - public OwnedSubData13079 SubData { get; set; } - } - - private class OwnedSubData13079 - { - public int Property { get; set; } - } - #endregion - #region Bug13587 + #region Issue15204 + + private MemberInfo GetMemberInfo(Type type, string name) + => type.GetProperty(name); [ConditionalFact] - public virtual void Type_casting_inside_sum() + public virtual async Task Null_check_removal_applied_recursively() { - using (CreateDatabase13587()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext13587(_options)) - { - var result = context.InventoryPools.Sum(p => (decimal)p.Quantity); + var userParam = Expression.Parameter(typeof(MyContext15204.TBuilding15204), "s"); + var builderProperty = Expression.MakeMemberAccess( + userParam, GetMemberInfo(typeof(MyContext15204.TBuilding15204), "Builder")); + var cityProperty = Expression.MakeMemberAccess( + builderProperty, GetMemberInfo(typeof(MyContext15204.TBuilder15204), "City")); + var nameProperty = Expression.MakeMemberAccess(cityProperty, GetMemberInfo(typeof(MyContext15204.TCity15204), "Name")); - AssertSql( - $@"SELECT SUM(IIF(`i`.`Quantity` IS NULL, NULL, CCUR(`i`.`Quantity`))) -FROM `InventoryPools` AS `i`"); - } - } - } + //{s => (IIF((IIF((s.Builder == null), null, s.Builder.City) == null), null, s.Builder.City.Name) == "Leeds")} + var selection = Expression.Lambda>( + Expression.Equal( + Expression.Condition( + Expression.Equal( + Expression.Condition( + Expression.Equal( + builderProperty, + Expression.Constant(null, typeof(MyContext15204.TBuilder15204))), + Expression.Constant(null, typeof(MyContext15204.TCity15204)), + cityProperty), + Expression.Constant(null, typeof(MyContext15204.TCity15204))), + Expression.Constant(null, typeof(string)), + nameProperty), + Expression.Constant("Leeds", typeof(string))), + userParam); - private JetTestStore CreateDatabase13587() - { - return CreateTestStore( - () => new MyContext13587(_options), - context => - { - context.InventoryPools.Add( - new InventoryPool13587 { Quantity = 2 }); + var query = context.BuildingSet + .Where(selection) + .Include(a => a.Builder).ThenInclude(a => a.City) + .Include(a => a.Mandator).ToList(); - context.SaveChanges(); + Assert.True(query.Count == 1); + Assert.True(query.First().Builder.City.Name == "Leeds"); + Assert.True(query.First().LongName == "Two L2"); - ClearLog(); - }); + AssertSql( +""" +SELECT `b`.`Id`, `b`.`BuilderId`, `b`.`Identity`, `b`.`LongName`, `b`.`MandatorId`, `b0`.`Id`, `b0`.`CityId`, `b0`.`Name`, `c`.`Id`, `c`.`Name`, `m`.`Id`, `m`.`Identity`, `m`.`Name` +FROM ((`BuildingSet` AS `b` +INNER JOIN `Builder` AS `b0` ON `b`.`BuilderId` = `b0`.`Id`) +LEFT JOIN `City` AS `c` ON `b0`.`CityId` = `c`.`Id`) +INNER JOIN `MandatorSet` AS `m` ON `b`.`MandatorId` = `m`.`Id` +WHERE (`c`.`Name` = 'Leeds') AND (`b0`.`CityId` IS NOT NULL AND `c`.`Id` IS NOT NULL) +"""); + } } - private class MyContext13587 : DbContext + protected class MyContext15204 : DbContext { - public virtual DbSet InventoryPools { get; set; } + public DbSet MandatorSet { get; set; } + public DbSet BuildingSet { get; set; } + public DbSet Builder { get; set; } + public DbSet City { get; set; } - public MyContext13587(DbContextOptions options) + public MyContext15204(DbContextOptions options) : base(options) { + ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + ChangeTracker.AutoDetectChangesEnabled = false; } - } - private class InventoryPool13587 - { - public int Id { get; set; } - public double Quantity { get; set; } - } + public void Seed() + { + var london = new TCity15204 { Name = "London" }; + var sam = new TBuilder15204 { Name = "Sam", City = london }; - #endregion + MandatorSet.Add( + new TMandator15204 + { + Identity = Guid.NewGuid(), + Name = "One", + Buildings = new List + { + new() + { + Identity = Guid.NewGuid(), + LongName = "One L1", + Builder = sam + }, + new() + { + Identity = Guid.NewGuid(), + LongName = "One L2", + Builder = sam + } + } + }); + MandatorSet.Add( + new TMandator15204 + { + Identity = Guid.NewGuid(), + Name = "Two", + Buildings = new List + { + new() + { + Identity = Guid.NewGuid(), + LongName = "Two L1", + Builder = new TBuilder15204 { Name = "John", City = london } + }, + new() + { + Identity = Guid.NewGuid(), + LongName = "Two L2", + Builder = new TBuilder15204 { Name = "Mark", City = new TCity15204 { Name = "Leeds" } } + } + } + }); - #region Bug12518 + SaveChanges(); + } - [ConditionalFact] - public virtual void Projecting_entity_with_value_converter_and_include_works() - { - using (CreateDatabase12518()) + public class TBuilding15204 { - using (var context = new MyContext12518(_options)) - { - var result = context.Parents.Include(p => p.Child).FirstOrDefault(); - - AssertSql( - $@"SELECT TOP 1 `p`.`Id`, `p`.`ChildId`, `c`.`Id`, `c`.`ParentId`, `c`.`ULongRowVersion` -FROM `Parents` AS `p` -LEFT JOIN `Children` AS `c` ON `p`.`ChildId` = `c`.`Id`"); - } + public int Id { get; set; } + public Guid Identity { get; set; } + public string LongName { get; set; } + public int BuilderId { get; set; } + public TBuilder15204 Builder { get; set; } + public TMandator15204 Mandator { get; set; } + public int MandatorId { get; set; } } - } - - private JetTestStore CreateDatabase12518() - { - return CreateTestStore( - () => new MyContext12518(_options), - context => - { - context.Parents.Add(new Parent12518()); - context.SaveChanges(); - - ClearLog(); - }); - } - - private class MyContext12518 : DbContext - { - public virtual DbSet Parents { get; set; } - public virtual DbSet Children { get; set; } - public MyContext12518(DbContextOptions options) - : base(options) + public class TBuilder15204 { + public int Id { get; set; } + public string Name { get; set; } + public int CityId { get; set; } + public TCity15204 City { get; set; } } - protected override void OnModelCreating(ModelBuilder modelBuilder) + public class TCity15204 { - var child = modelBuilder.Entity(); - child.HasOne(_ => _.Parent) - .WithOne(_ => _.Child) - .HasForeignKey(_ => _.ChildId); - child.Property(x => x.ULongRowVersion) - .HasConversion(new NumberToBytesConverter()) - .IsRowVersion() - .IsRequired() - .HasColumnType("RowVersion"); - - modelBuilder.Entity(); + public int Id { get; set; } + public string Name { get; set; } } - } - - private class Parent12518 - { - public Guid Id { get; set; } = Guid.NewGuid(); - public Guid? ChildId { get; set; } - public Child12518 Child { get; set; } - } - private class Child12518 - { - public Guid Id { get; set; } = Guid.NewGuid(); - public ulong ULongRowVersion { get; set; } - public Guid ParentId { get; set; } - public Parent12518 Parent { get; set; } + public class TMandator15204 + { + public int Id { get; set; } + public Guid Identity { get; set; } + public string Name { get; set; } + public virtual ICollection Buildings { get; set; } + } } #endregion - #region Bug12549 + #region Issue15518 - [ConditionalFact] - public virtual void Union_and_insert_12549() + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual async Task Nested_queries_does_not_cause_concurrency_exception_sync(bool tracking) { - using (CreateDatabase12549()) - { - using (var context = new MyContext12549(_options)) - { - var id1 = 1; - var id2 = 2; + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - var ids1 = context.Set() - .Where(x => x.Id == id1) - .Select(x => x.Id); + using (var context = contextFactory.CreateContext()) + { + var query = context.Repos.OrderBy(r => r.Id).Where(r => r.Id > 0); + query = tracking ? query.AsTracking() : query.AsNoTracking(); - var ids2 = context.Set() - .Where(x => x.Id == id2) - .Select(x => x.Id); + foreach (var a in query) + { + foreach (var b in query) + { + } + } + } - var results = ids1.Union(ids2).ToList(); + using (var context = contextFactory.CreateContext()) + { + var query = context.Repos.OrderBy(r => r.Id).Where(r => r.Id > 0); + query = tracking ? query.AsTracking() : query.AsNoTracking(); - context.AddRange(new Table1_12549(), new Table2_12549(), new Table1_12549(), new Table2_12549()); - context.SaveChanges(); + await foreach (var a in query.AsAsyncEnumerable()) + { + await foreach (var b in query.AsAsyncEnumerable()) + { + } } } } - private JetTestStore CreateDatabase12549() - => CreateTestStore(() => new MyContext12549(_options), context => { }); - - private class MyContext12549 : DbContext + protected class MyContext15518 : DbContext { - public DbSet Table1 { get; set; } - public DbSet Table2 { get; set; } + public DbSet Repos { get; set; } - public MyContext12549(DbContextOptions options) + public MyContext15518(DbContextOptions options) : base(options) { } - } - private class Table1_12549 - { - public int Id { get; set; } - } + public void Seed() + { + AddRange( + new Repo15518 { Name = "London" }, + new Repo15518 { Name = "New York" }); - private class Table2_12549 - { - public int Id { get; set; } + SaveChanges(); + } + + public class Repo15518 + { + public int Id { get; set; } + public string Name { get; set; } + } } #endregion - #region Bug16233 + #region Issue8864 [ConditionalFact] - public virtual void Derived_reference_is_skipped_when_base_type() + public virtual async Task Select_nested_projection() { - using (CreateDatabase16233()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext16233(_options)) - { - var result = context.Bases.Include(p => ((DerivedType16233)p).Reference).OrderBy(b => b.Id).ToList(); + var customers = context.Customers + .Select(c => new { Customer = c, CustomerAgain = MyContext8864.Get(context, c.Id) }) + .ToList(); - Assert.Equal(3, result.Count); - Assert.NotNull(Assert.IsType(result[1]).Reference); - Assert.Null(Assert.IsType(result[2]).Reference); - Assert.True(context.Entry(Assert.IsType(result[2])).Reference("Reference").IsLoaded); + Assert.Equal(2, customers.Count); - AssertSql( - $@"SELECT `b`.`Id`, `b`.`Discriminator`, `r`.`Id`, `r`.`DerivedTypeId` -FROM `Bases` AS `b` -LEFT JOIN `Reference16233` AS `r` ON `b`.`Id` = `r`.`DerivedTypeId` -WHERE `b`.`Discriminator` IN ('BaseType16233', 'DerivedType16233') -ORDER BY `b`.`Id`"); + foreach (var customer in customers) + { + Assert.Same(customer.Customer, customer.CustomerAgain); } } } - [ConditionalFact] - public virtual void Derived_reference_is_skipped_when_base_type_no_tracking() + protected class MyContext8864 : DbContext { - using (CreateDatabase16233()) + public DbSet Customers { get; set; } + + public MyContext8864(DbContextOptions options) + : base(options) { - using (var context = new MyContext16233(_options)) - { - var result = context.Bases.AsNoTracking().Include(p => ((DerivedType16233)p).Reference).OrderBy(b => b.Id).ToList(); + } - Assert.Equal(3, result.Count); - Assert.NotNull(Assert.IsType(result[1]).Reference); - Assert.NotNull(Assert.IsType(result[1]).Reference.DerivedType); - Assert.Null(Assert.IsType(result[2]).Reference); + public void Seed() + { + AddRange( + new Customer8864 { Name = "Alan" }, + new Customer8864 { Name = "Elon" }); - AssertSql( - $@"SELECT `b`.`Id`, `b`.`Discriminator`, `r`.`Id`, `r`.`DerivedTypeId` -FROM `Bases` AS `b` -LEFT JOIN `Reference16233` AS `r` ON `b`.`Id` = `r`.`DerivedTypeId` -WHERE `b`.`Discriminator` IN ('BaseType16233', 'DerivedType16233') -ORDER BY `b`.`Id`"); - } + SaveChanges(); + } + + public static Customer8864 Get(MyContext8864 context, int id) + => context.Customers.Single(c => c.Id == id); + + public class Customer8864 + { + public int Id { get; set; } + public string Name { get; set; } } } - private JetTestStore CreateDatabase16233() + #endregion + + #region Issue7983 + + [ConditionalFact] + public virtual async Task New_instances_in_projection_are_not_shared_across_results() { - return CreateTestStore( - () => new MyContext16233(_options), - context => - { - context.AddRange( - new BaseType16233(), - new DerivedType16233 { Reference = new Reference16233() }, - new DerivedType16233()); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - context.SaveChanges(); + using (var context = contextFactory.CreateContext()) + { + var list = context.Posts.Select(p => new MyContext7983.PostDTO7983().From(p)).ToList(); - ClearLog(); - }); + Assert.Equal(3, list.Count); + Assert.Equal(new[] { "First", "Second", "Third" }, list.Select(dto => dto.Title)); + + AssertSql( +""" +SELECT `p`.`Id`, `p`.`BlogId`, `p`.`Title` +FROM `Posts` AS `p` +"""); + } } - private class MyContext16233 : DbContext + protected class MyContext7983 : DbContext { - public virtual DbSet Bases { get; set; } - public virtual DbSet Derived { get; set; } + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } - public MyContext16233(DbContextOptions options) + public MyContext7983(DbContextOptions options) : base(options) { } - } - private class BaseType16233 - { - public int Id { get; set; } - } + public void Seed() + { + Add( + new Blog7983 + { + Posts = new List + { + new() { Title = "First" }, + new() { Title = "Second" }, + new() { Title = "Third" } + } + }); - private class DerivedType16233 : BaseType16233 - { - public Reference16233 Reference { get; set; } - } + SaveChanges(); + } - private class Reference16233 - { - public int Id { get; set; } - public int DerivedTypeId { get; set; } - public DerivedType16233 DerivedType { get; set; } - } + public class Blog7983 + { + public int Id { get; set; } + public string Title { get; set; } - #endregion + public ICollection Posts { get; set; } + } - #region Bug15684 + public class Post7983 + { + public int Id { get; set; } + public string Title { get; set; } - [ConditionalFact] - public virtual void Projection_failing_with_EnumToStringConverter() - { - using (CreateDatabase15684()) + public int? BlogId { get; set; } + public Blog7983 Blog { get; set; } + } + + public class PostDTO7983 { - using (var context = new MyContext15684(_options)) + public string Title { get; set; } + + public PostDTO7983 From(Post7983 post) { - var query = from p in context.Products - join c in context.Categories on p.CategoryId equals c.Id into grouping - from c in grouping.DefaultIfEmpty() - select new ProductDto15684 - { - Id = p.Id, - Name = p.Name, - CategoryName = c == null ? "Other" : c.Name, - CategoryStatus = c == null ? CategoryStatus15684.Active : c.Status - }; - var result = query.ToList(); - Assert.Equal(2, result.Count); - AssertSql( - $@"SELECT `p`.`Id`, `p`.`Name`, CASE - WHEN `c`.`Id` IS NULL THEN 'Other' - ELSE `c`.`Name` -END AS `CategoryName`, CASE - WHEN `c`.`Id` IS NULL THEN 'Active' - ELSE `c`.`Status` -END AS `CategoryStatus` -FROM `Products` AS `p` -LEFT JOIN `Categories` AS `c` ON `p`.`CategoryId` = `c`.`Id`"); + Title = post.Title; + return this; } } } - private JetTestStore CreateDatabase15684() - => CreateTestStore( - () => new MyContext15684(_options), - context => - { - context.Products.Add( - new Product15684 - { - Name = "Apple", - Category = new Category15684 { Name = "Fruit", Status = CategoryStatus15684.Active } - }); - - context.Products.Add(new Product15684 { Name = "Bike" }); - - context.SaveChanges(); + #endregion - ClearLog(); - }); + #region Issue17253 - private class MyContext15684 : DbContext + [ConditionalFact] + public virtual async Task Self_reference_in_query_filter_works() { - public DbSet Categories { get; set; } - public DbSet Products { get; set; } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - public MyContext15684(DbContextOptions options) - : base(options) + using (var context = contextFactory.CreateContext()) { + var query = context.EntitiesWithQueryFilterSelfReference.Where(e => e.Name != "Foo"); + var result = query.ToList(); + + AssertSql( +""" +SELECT `e`.`Id`, `e`.`Name` +FROM `EntitiesWithQueryFilterSelfReference` AS `e` +WHERE EXISTS ( + SELECT 1 + FROM `EntitiesWithQueryFilterSelfReference` AS `e0`) AND (`e`.`Name` <> 'Foo' OR `e`.`Name` IS NULL) +"""); } - protected override void OnModelCreating(ModelBuilder modelBuilder) + using (var context = contextFactory.CreateContext()) { - modelBuilder - .Entity() - .Property(e => e.Status) - .HasConversion(new EnumToStringConverter()); + ClearLog(); + var query = context.EntitiesReferencingEntityWithQueryFilterSelfReference.Where(e => e.Name != "Foo"); + var result = query.ToList(); + + AssertSql( +""" +SELECT `e`.`Id`, `e`.`Name` +FROM `EntitiesReferencingEntityWithQueryFilterSelfReference` AS `e` +WHERE EXISTS ( + SELECT 1 + FROM `EntitiesWithQueryFilterSelfReference` AS `e0` + WHERE EXISTS ( + SELECT 1 + FROM `EntitiesWithQueryFilterSelfReference` AS `e1`)) AND (`e`.`Name` <> 'Foo' OR `e`.`Name` IS NULL) +"""); } } - private class Product15684 + protected class MyContext17253 : DbContext { - [Key] - public int Id { get; set; } - - [Required] - public string Name { get; set; } + public DbSet EntitiesWithQueryFilterSelfReference { get; set; } - public int? CategoryId { get; set; } + public DbSet EntitiesReferencingEntityWithQueryFilterSelfReference + { + get; + set; + } - public Category15684 Category { get; set; } - } + public DbSet EntitiesWithQueryFilterCycle1 { get; set; } + public DbSet EntitiesWithQueryFilterCycle2 { get; set; } + public DbSet EntitiesWithQueryFilterCycle3 { get; set; } - private class Category15684 - { - [Key] - public int Id { get; set; } + public MyContext17253(DbContextOptions options) + : base(options) + { + } - [Required] - public string Name { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterSelfReference.Any()); + modelBuilder.Entity() + .HasQueryFilter(e => Set().Any()); - public CategoryStatus15684 Status { get; set; } - } + modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterCycle2.Any()); + modelBuilder.Entity().HasQueryFilter(e => Set().Any()); + modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterCycle1.Any()); + } - private class ProductDto15684 - { - public string CategoryName { get; set; } - public CategoryStatus15684 CategoryStatus { get; set; } - public int Id { get; set; } - public string Name { get; set; } - } + public void Seed() + { + EntitiesWithQueryFilterSelfReference.Add( + new EntityWithQueryFilterSelfReference { Name = "EntityWithQueryFilterSelfReference" }); + EntitiesReferencingEntityWithQueryFilterSelfReference.Add( + new EntityReferencingEntityWithQueryFilterSelfReference { Name = "EntityReferencingEntityWithQueryFilterSelfReference" }); - public enum CategoryStatus15684 - { - Active = 0, - Removed = 1 - } + EntitiesWithQueryFilterCycle1.Add(new EntityWithQueryFilterCycle1 { Name = "EntityWithQueryFilterCycle1_1" }); + EntitiesWithQueryFilterCycle2.Add(new EntityWithQueryFilterCycle2 { Name = "EntityWithQueryFilterCycle2_1" }); + EntitiesWithQueryFilterCycle3.Add(new EntityWithQueryFilterCycle3 { Name = "EntityWithQueryFilterCycle3_1" }); - #endregion + SaveChanges(); + } - #region Bug15204 + public class EntityWithQueryFilterSelfReference + { + public int Id { get; set; } + public string Name { get; set; } + } - private MemberInfo GetMemberInfo(Type type, string name) + public class EntityReferencingEntityWithQueryFilterSelfReference + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class EntityWithQueryFilterCycle1 + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class EntityWithQueryFilterCycle2 + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class EntityWithQueryFilterCycle3 + { + public int Id { get; set; } + public string Name { get; set; } + } + } + + #endregion + + #region Issue17276_17099_16759 + + [ConditionalFact] + public virtual async Task Expression_tree_constructed_via_interface_works_17276() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + var query = MyContext17276.List17276(context.RemovableEntities); + + AssertSql( +""" +SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `r`.`OwnedEntity_Exists`, `r`.`OwnedEntity_OwnedValue` +FROM `RemovableEntities` AS `r` +WHERE `r`.`IsRemoved` <> TRUE +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.Parents + .Where(p => EF.Property(EF.Property(p, "RemovableEntity"), "IsRemoved")) + .ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `p`.`RemovableEntityId` +FROM `Parents` AS `p` +LEFT JOIN `RemovableEntities` AS `r` ON `p`.`RemovableEntityId` = `r`.`Id` +WHERE `r`.`IsRemoved` = TRUE +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.RemovableEntities + .Where(p => EF.Property(EF.Property(p, "OwnedEntity"), "OwnedValue") == "Abc") + .ToList(); + + AssertSql( +""" +SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `r`.`OwnedEntity_Exists`, `r`.`OwnedEntity_OwnedValue` +FROM `RemovableEntities` AS `r` +WHERE `r`.`OwnedEntity_OwnedValue` = 'Abc' +"""); + } + + // #16759 + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var specification = new MyContext17276.Specification17276(1); + var entities = context.Set().Where(specification.Criteria).ToList(); + + AssertSql( +""" +@__id_0='1' + +SELECT `p`.`Id`, `p`.`RemovableEntityId` +FROM `Parents` AS `p` +WHERE `p`.`Id` = @__id_0 +"""); + } + } + + protected class MyContext17276 : DbContext + { + public DbSet RemovableEntities { get; set; } + public DbSet Parents { get; set; } + + public MyContext17276(DbContextOptions options) + : base(options) + { + } + + public static List List17276(IQueryable query) + where T : IRemovable17276 + => query.Where(x => !x.IsRemoved).ToList(); + + public interface IRemovable17276 + { + bool IsRemoved { get; set; } + + string RemovedByUser { get; set; } + + DateTime? Removed { get; set; } + } + + public class RemovableEntity17276 : IRemovable17276 + { + public int Id { get; set; } + public bool IsRemoved { get; set; } + public string RemovedByUser { get; set; } + public DateTime? Removed { get; set; } + public OwnedEntity OwnedEntity { get; set; } + } + + public class Parent17276 : IHasId17276 + { + public int Id { get; set; } + public RemovableEntity17276 RemovableEntity { get; set; } + } + + [Owned] + public class OwnedEntity : IOwned + { + public string OwnedValue { get; set; } + public int Exists { get; set; } + } + + public interface IHasId17276 + { + T Id { get; } + } + + public interface IOwned + { + string OwnedValue { get; } + int Exists { get; } + } + + public class Specification17276 + where T : IHasId17276 + { + public Expression> Criteria { get; } + + public Specification17276(int id) + { + Criteria = t => t.Id == id; + } + } + } + + #endregion + + #region Issue6864 + + [ConditionalFact] + public virtual async Task Implicit_cast_6864() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + // Verify no client eval + var result = context.Foos.Where(f => f.String == new MyContext6864.Bar6864(1337)).ToList(); + + Assert.Empty(result); + + AssertSql( +""" +SELECT `f`.`Id`, `f`.`String` +FROM `Foos` AS `f` +WHERE `f`.`String` = '1337' +"""); + } + + //Access_property_of_closure + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + // Verify no client eval + var bar = new MyContext6864.Bar6864(1337); + var result = context.Foos.Where(f => f.String == bar.Value).ToList(); + + Assert.Empty(result); + + AssertSql( +""" +@__bar_Value_0='1337' (Size = 255) + +SELECT `f`.`Id`, `f`.`String` +FROM `Foos` AS `f` +WHERE `f`.`String` = @__bar_Value_0 +"""); + } + + //Implicitly_cast_closure + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + // Verify no client eval + var bar = new MyContext6864.Bar6864(1337); + var result = context.Foos.Where(f => f.String == bar.ToString()).ToList(); + + Assert.Empty(result); + + AssertSql( +""" +@__ToString_0='1337' (Size = 255) + +SELECT `f`.`Id`, `f`.`String` +FROM `Foos` AS `f` +WHERE `f`.`String` = @__ToString_0 +"""); + } + + //Implicitly_cast_closure + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + // Verify no client eval + var bar = new MyContext6864.Bar6864(1337); + var result = context.Foos.Where(f => f.String == bar).ToList(); + + Assert.Empty(result); + + AssertSql( +""" +@__p_0='1337' (Size = 255) + +SELECT `f`.`Id`, `f`.`String` +FROM `Foos` AS `f` +WHERE `f`.`String` = @__p_0 +"""); + } + + // Implicitly_cast_return_value + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + // Verify no client eval + var result = context.Foos.Where(f => f.String == new MyContext6864.Bar6864(1337).Clone()).ToList(); + + Assert.Empty(result); + + AssertSql( +""" +SELECT `f`.`Id`, `f`.`String` +FROM `Foos` AS `f` +WHERE `f`.`String` = '1337' +"""); + } + } + + private class MyContext6864 : DbContext + { + public DbSet Foos { get; set; } + + public MyContext6864(DbContextOptions options) + : base(options) + { + } + + public class FooEntity6864 + { + public int Id { get; set; } + public string String { get; set; } + } + + public class Bar6864 + { + private readonly int _value; + + public Bar6864(int value) + { + _value = value; + } + + public string Value + => _value.ToString(); + + public override string ToString() + => Value; + + public static implicit operator string(Bar6864 bar) + => bar.Value; + + public Bar6864 Clone() + => new(_value); + } + } + + #endregion + + #region Issue9582 + + [ConditionalFact] + public virtual async Task Setting_IsUnicode_generates_unicode_literal_in_SQL() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + // Verify SQL + var query = context.Set().Where(xx => xx.Nombre.Contains("lla")).ToList(); + + AssertSql( +""" +SELECT `t`.`Id`, `t`.`Nombre` +FROM `TipoServicio9582` AS `t` +WHERE `t`.`Nombre` LIKE '%lla%' +"""); + } + } + + protected class MyContext9582 : DbContext + { + public MyContext9582(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + builder => + { + builder.HasKey(ts => ts.Id); + + builder.Property(ts => ts.Id).IsRequired(); + builder.Property(ts => ts.Nombre).IsRequired().HasMaxLength(20); + }); + + foreach (var property in modelBuilder.Model.GetEntityTypes() + .SelectMany(e => e.GetProperties().Where(p => p.ClrType == typeof(string)))) + { + property.SetIsUnicode(false); + } + } + + public class TipoServicio9582 + { + public int Id { get; set; } + public string Nombre { get; set; } + } + } + + #endregion + + #region Issue7222 + + [ConditionalFact] + public virtual async Task Inlined_dbcontext_is_not_leaking() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + var entities = context.Blogs.Select(b => context.ClientMethod(b)).ToList(); + + AssertSql( +""" +SELECT `b`.`Id` +FROM `Blogs` AS `b` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + Assert.Throws(() => context.RunQuery()); + } + } + + protected class MyContext7222 : DbContext + { + public DbSet Blogs { get; set; } + + public MyContext7222(DbContextOptions options) + : base(options) + { + } + + public void RunQuery() + => Blogs.Select(b => ClientMethod(b)).ToList(); + + public int ClientMethod(Blog7222 blog) + => blog.Id; + + public class Blog7222 + { + public int Id { get; set; } + } + } + + #endregion + + #region Issue17644 + + [ConditionalFact] + public virtual async Task Return_type_of_singular_operator_is_preserved() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .FirstAsync(); + + AssertSql( +""" +SELECT TOP 1 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .FirstOrDefaultAsync(); + + AssertSql( +""" +SELECT TOP 1 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .SingleAsync(); + + AssertSql( +""" +SELECT TOP 2 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .SingleOrDefaultAsync(); + + AssertSql( +""" +SELECT TOP 2 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .OrderBy(p => p.Id) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .LastAsync(); + + AssertSql( +""" +SELECT TOP 1 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +ORDER BY `p`.`Id` DESC +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var personsToFind = await context.Persons.Where(p => p.Age >= 21) + .OrderBy(p => p.Id) + .Select(p => new MyContext17644.PersonDetailView17644 { Name = p.Name, Age = p.Age }) + .LastOrDefaultAsync(); + + AssertSql( +""" +SELECT TOP 1 `p`.`Name`, `p`.`Age` +FROM `Persons` AS `p` +WHERE `p`.`Age` >= 21 +ORDER BY `p`.`Id` DESC +"""); + } + } + + protected class MyContext17644 : DbContext + { + public DbSet Persons { get; set; } + + public MyContext17644(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + var person = new Person17644 { Name = "John Doe", Age = 21 }; + Persons.Add(person); + SaveChanges(); + } + + public class Person17644 + { + public int Id { get; set; } + public string Name { set; get; } + public int Age { set; get; } + } + + public class PersonView17644 + { + public string Name { set; get; } + } + + public class PersonDetailView17644 : PersonView17644 + { + public int Age { set; get; } + } + } + + #endregion + + #region Issue11023 + + [ConditionalFact] + public virtual async Task Async_correlated_projection_with_first() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = await context.Entities + .Select(e => new { ThingIds = e.Values.First().Things.Select(t => t.Subthing.ThingId).ToList() }) + .ToListAsync(); + + var result = Assert.Single(query); + Assert.Equal(new[] { 1, 2 }, result.ThingIds); + + AssertSql( + """ +SELECT [e].[Id], [t0].[ThingId], [t0].[Id], [t0].[Id0] +FROM [Entities] AS [e] +OUTER APPLY ( + SELECT [s].[ThingId], [t].[Id], [s].[Id] AS [Id0] + FROM [Things] AS [t] + LEFT JOIN [Subthings] AS [s] ON [t].[Id] = [s].[ThingId] + WHERE ( + SELECT TOP(1) [v].[Id] + FROM [Values] AS [v] + WHERE [e].[Id] = [v].[Entity11023Id]) IS NOT NULL AND (( + SELECT TOP(1) [v0].[Id] + FROM [Values] AS [v0] + WHERE [e].[Id] = [v0].[Entity11023Id]) = [t].[Value11023Id] OR (( + SELECT TOP(1) [v0].[Id] + FROM [Values] AS [v0] + WHERE [e].[Id] = [v0].[Entity11023Id]) IS NULL AND [t].[Value11023Id] IS NULL)) +) AS [t0] +ORDER BY [e].[Id], [t0].[Id] +"""); + } + } + + protected class MyContext11023 : DbContext + { + public DbSet Entities { get; set; } + public DbSet Values { get; set; } + public DbSet Things { get; set; } + public DbSet Subthings { get; set; } + + public MyContext11023(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + Add( + new Entity11023 + { + Values = new List + { + new() + { + Things = new List + { + new() { Subthing = new Subthing11023() }, new() { Subthing = new Subthing11023() } + } + } + } + }); + + SaveChanges(); + } + + public class Entity11023 + { + public int Id { get; set; } + public ICollection Values { get; set; } + } + + public class Value11023 + { + public int Id { get; set; } + public ICollection Things { get; set; } + } + + public class Thing11023 + { + public int Id { get; set; } + public Subthing11023 Subthing { get; set; } + } + + public class Subthing11023 + { + public int Id { get; set; } + public int ThingId { get; set; } + public Thing11023 Thing { get; set; } + } + } + + #endregion + + #region Issue7973 + + [ConditionalFact] + public virtual async Task SelectMany_with_collection_selector_having_subquery() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var users = (from user in context.Users + from organisation in context.Organisations.Where(o => o.OrganisationUsers.Any()).DefaultIfEmpty() + select new { UserId = user.Id, OrgId = organisation.Id }).ToList(); + + Assert.Equal(2, users.Count); + + AssertSql( + """ +SELECT [u].[Id] AS [UserId], [t0].[Id] AS [OrgId] +FROM [Users] AS [u] +CROSS JOIN ( + SELECT [t].[Id] + FROM ( + SELECT NULL AS [empty] + ) AS [e] + LEFT JOIN ( + SELECT [o].[Id] + FROM [Organisations] AS [o] + WHERE EXISTS ( + SELECT 1 + FROM [OrganisationUser7973] AS [o0] + WHERE [o].[Id] = [o0].[OrganisationId]) + ) AS [t] ON 1 = 1 +) AS [t0] +"""); + } + } + + protected class MyContext7973 : DbContext + { + public DbSet Users { get; set; } + public DbSet Organisations { get; set; } + + public MyContext7973(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(ou => new { ou.OrganisationId, ou.UserId }); + modelBuilder.Entity().HasOne(ou => ou.Organisation).WithMany(o => o.OrganisationUsers) + .HasForeignKey(ou => ou.OrganisationId); + modelBuilder.Entity().HasOne(ou => ou.User).WithMany(u => u.OrganisationUsers) + .HasForeignKey(ou => ou.UserId); + } + + public void Seed() + { + AddRange( + new OrganisationUser7973 { Organisation = new Organisation7973(), User = new User7973() }, + new Organisation7973(), + new User7973()); + + SaveChanges(); + } + + public class User7973 + { + public int Id { get; set; } + public List OrganisationUsers { get; set; } + } + + public class Organisation7973 + { + public int Id { get; set; } + public List OrganisationUsers { get; set; } + } + + public class OrganisationUser7973 + { + public int OrganisationId { get; set; } + public Organisation7973 Organisation { get; set; } + + public int UserId { get; set; } + public User7973 User { get; set; } + } + } + + #endregion + + #region Issue10447 + + [ConditionalFact] + public virtual async Task Nested_include_queries_do_not_populate_navigation_twice() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Blogs.Include(b => b.Posts); + + foreach (var blog in query) + { + query.ToList(); + } + + Assert.Collection( + query, + b => Assert.Equal(3, b.Posts.Count), + b => Assert.Equal(2, b.Posts.Count), + b => Assert.Single(b.Posts)); + } + } + + protected class MyContext10447 : DbContext + { + public DbSet Blogs { get; set; } + + public MyContext10447(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + public void Seed() + { + AddRange( + new Blog10447 + { + Posts = new List + { + new(), + new(), + new() + } + }, + new Blog10447 { Posts = new List { new(), new() } }, + new Blog10447 { Posts = new List { new() } }); + + SaveChanges(); + } + + public class Blog10447 + { + public int Id { get; set; } + public List Posts { get; set; } + } + + public class Post10447 + { + public int Id { get; set; } + + public Blog10447 Blog { get; set; } + } + } + + #endregion + + #region Issue12456 + + [ConditionalFact] + public virtual async Task Let_multiple_references_with_reference_to_outer() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + var users = (from a in context.Activities + let cs = context.CompetitionSeasons + .First(s => s.StartDate <= a.DateTime && a.DateTime < s.EndDate) + select new { cs.Id, Points = a.ActivityType.Points.Where(p => p.CompetitionSeason == cs) }).ToList(); + + AssertSql( + """ +SELECT ( + SELECT TOP(1) [c].[Id] + FROM [CompetitionSeasons] AS [c] + WHERE [c].[StartDate] <= [a].[DateTime] AND [a].[DateTime] < [c].[EndDate]), [a].[Id], [a0].[Id], [t].[Id], [t].[ActivityTypeId], [t].[CompetitionSeasonId], [t].[Points], [t].[Id0] +FROM [Activities] AS [a] +INNER JOIN [ActivityType12456] AS [a0] ON [a].[ActivityTypeId] = [a0].[Id] +OUTER APPLY ( + SELECT [a1].[Id], [a1].[ActivityTypeId], [a1].[CompetitionSeasonId], [a1].[Points], [c0].[Id] AS [Id0] + FROM [ActivityTypePoints12456] AS [a1] + INNER JOIN [CompetitionSeasons] AS [c0] ON [a1].[CompetitionSeasonId] = [c0].[Id] + WHERE [a0].[Id] = [a1].[ActivityTypeId] AND [c0].[Id] = ( + SELECT TOP(1) [c1].[Id] + FROM [CompetitionSeasons] AS [c1] + WHERE [c1].[StartDate] <= [a].[DateTime] AND [a].[DateTime] < [c1].[EndDate]) +) AS [t] +ORDER BY [a].[Id], [a0].[Id], [t].[Id] +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var users = context.Activities + .Select( + a => new + { + Activity = a, + CompetitionSeason = context.CompetitionSeasons + .First(s => s.StartDate <= a.DateTime && a.DateTime < s.EndDate) + }) + .Select( + a => new + { + a.Activity, + CompetitionSeasonId = a.CompetitionSeason.Id, + Points = a.Activity.Points + ?? a.Activity.ActivityType.Points + .Where(p => p.CompetitionSeason == a.CompetitionSeason) + .Select(p => p.Points).SingleOrDefault() + }).ToList(); + + AssertSql( + """ +SELECT [a].[Id], [a].[ActivityTypeId], [a].[DateTime], [a].[Points], ( + SELECT TOP(1) [c].[Id] + FROM [CompetitionSeasons] AS [c] + WHERE [c].[StartDate] <= [a].[DateTime] AND [a].[DateTime] < [c].[EndDate]) AS [CompetitionSeasonId], COALESCE([a].[Points], ( + SELECT TOP(1) [a1].[Points] + FROM [ActivityTypePoints12456] AS [a1] + INNER JOIN [CompetitionSeasons] AS [c0] ON [a1].[CompetitionSeasonId] = [c0].[Id] + WHERE [a0].[Id] = [a1].[ActivityTypeId] AND [c0].[Id] = ( + SELECT TOP(1) [c1].[Id] + FROM [CompetitionSeasons] AS [c1] + WHERE [c1].[StartDate] <= [a].[DateTime] AND [a].[DateTime] < [c1].[EndDate])), 0) AS [Points] +FROM [Activities] AS [a] +INNER JOIN [ActivityType12456] AS [a0] ON [a].[ActivityTypeId] = [a0].[Id] +"""); + } + } + + private class MyContext12456 : DbContext + { + public DbSet Activities { get; set; } + public DbSet CompetitionSeasons { get; set; } + + public MyContext12456(DbContextOptions options) + : base(options) + { + } + + public class CompetitionSeason12456 + { + public int Id { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public List ActivityTypePoints { get; set; } + } + + public class Point12456 + { + public int Id { get; set; } + public CompetitionSeason12456 CompetitionSeason { get; set; } + public int? Points { get; set; } + } + + public class ActivityType12456 + { + public int Id { get; set; } + public List Points { get; set; } + } + + public class ActivityTypePoints12456 + { + public int Id { get; set; } + public int ActivityTypeId { get; set; } + public int CompetitionSeasonId { get; set; } + public int Points { get; set; } + + public ActivityType12456 ActivityType { get; set; } + public CompetitionSeason12456 CompetitionSeason { get; set; } + } + + public class Activity12456 + { + public int Id { get; set; } + public int ActivityTypeId { get; set; } + public DateTime DateTime { get; set; } + public int? Points { get; set; } + public ActivityType12456 ActivityType { get; set; } + } + } + + #endregion + + #region Issue15137 + + [ConditionalFact] + public virtual async Task Max_in_multi_level_nested_subquery() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var container = await context.Trades + .Select( + x => new + { + x.Id, + Assets = x.Assets.AsQueryable() + .Select( + y => new + { + y.Id, + Contract = new + { + y.Contract.Id, + Season = new + { + y.Contract.Season.Id, + IsPastTradeDeadline = + (y.Contract.Season.Games.Max(z => (int?)z.GameNumber) ?? 0) > 10 + } + } + }) + .ToList() + }) + .SingleAsync(); + + AssertSql( +""" +SELECT `t0`.`Id`, `t1`.`Id`, `t1`.`Id0`, `t1`.`Id1`, `t1`.`IsPastTradeDeadline` +FROM ( + SELECT TOP 2 `t`.`Id` + FROM `Trades` AS `t` +) AS `t0` +LEFT JOIN ( + SELECT `d`.`Id`, `d0`.`Id` AS `Id0`, `d1`.`Id` AS `Id1`, IIF(IIF(( + SELECT MAX(`d2`.`GameNumber`) + FROM `DbGame` AS `d2` + WHERE `d1`.`Id` IS NOT NULL AND `d1`.`Id` = `d2`.`SeasonId`) IS NULL, 0, ( + SELECT MAX(`d2`.`GameNumber`) + FROM `DbGame` AS `d2` + WHERE `d1`.`Id` IS NOT NULL AND `d1`.`Id` = `d2`.`SeasonId`)) > 10, TRUE, FALSE) AS `IsPastTradeDeadline`, `d`.`DbTradeId` + FROM (`DbTradeAsset` AS `d` + INNER JOIN `DbContract` AS `d0` ON `d`.`ContractId` = `d0`.`Id`) + LEFT JOIN `DbSeason` AS `d1` ON `d0`.`SeasonId` = `d1`.`Id` +) AS `t1` ON `t0`.`Id` = `t1`.`DbTradeId` +ORDER BY `t0`.`Id`, `t1`.`Id`, `t1`.`Id0` +"""); + } + } + + protected class MyContext15137 : DbContext + { + public DbSet Trades { get; set; } + + public MyContext15137(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + var dbTrade = new DbTrade + { + Assets = new List + { + new() + { + Contract = new DbContract + { + Season = new DbSeason { Games = new List { new() { GameNumber = 1 } } } + } + } + } + }; + + Trades.Add(dbTrade); + SaveChanges(); + } + + public class DbTrade + { + public int Id { get; set; } + public List Assets { get; set; } + } + + public class DbTradeAsset + { + public int Id { get; set; } + public int ContractId { get; set; } + + public DbContract Contract { get; set; } + } + + public class DbContract + { + public int Id { get; set; } + + public DbSeason Season { get; set; } + } + + public class DbSeason + { + public int Id { get; set; } + + public List Games { get; set; } + } + + public class DbGame + { + public int Id { get; set; } + public int GameNumber { get; set; } + + public DbSeason Season { get; set; } + } + } + + #endregion + + #region Issue13517 + + [ConditionalFact] + public async Task Query_filter_with_pk_fk_optimization_Issue_13517() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + context.Entities.Select( + s => + new IssueContext13517.IssueEntityDto13517 + { + Id = s.Id, + RefEntity = s.RefEntity == null + ? null + : new IssueContext13517.IssueRefEntityDto13517 { Id = s.RefEntity.Id, Public = s.RefEntity.Public }, + RefEntityId = s.RefEntityId + }).Single(p => p.Id == 1); + + AssertSql( +""" +SELECT TOP 2 `e`.`Id`, IIF(`t`.`Id` IS NULL, TRUE, FALSE), `t`.`Id`, `t`.`Public`, `e`.`RefEntityId` +FROM `Entities` AS `e` +LEFT JOIN ( + SELECT `r`.`Id`, `r`.`Public` + FROM `RefEntities` AS `r` + WHERE `r`.`Public` = TRUE +) AS `t` ON `e`.`RefEntityId` = `t`.`Id` +WHERE `e`.`Id` = 1 +"""); + } + } + + protected class IssueContext13517 : DbContext + { + public DbSet Entities { get; set; } + public DbSet RefEntities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasQueryFilter(f => f.Public); + + public IssueContext13517(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + var refEntity = new IssueRefEntity13517 { Public = false }; + RefEntities.Add(refEntity); + Entities.Add(new IssueEntity13517 { RefEntity = refEntity }); + SaveChanges(); + } + + public class IssueEntity13517 + { + public int Id { get; set; } + public int? RefEntityId { get; set; } + public IssueRefEntity13517 RefEntity { get; set; } + } + + public class IssueRefEntity13517 + { + public int Id { get; set; } + public bool Public { get; set; } + } + + public class IssueEntityDto13517 + { + public int Id { get; set; } + public int? RefEntityId { get; set; } + public IssueRefEntityDto13517 RefEntity { get; set; } + } + + public class IssueRefEntityDto13517 + { + public int Id { get; set; } + public bool Public { get; set; } + } + } + + #endregion + + #region Issue17794 + + [ConditionalFact] + public async Task Double_convert_interface_created_expression_tree() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var expression = + IssueContext17794.HasAction17794(IssueContext17794.OfferActions17794.Accepted); + var query = context.Offers.Where(expression).Count(); + + Assert.Equal(1, query); + + AssertSql( +""" +@__action_0='1' + +SELECT COUNT(*) +FROM `Offers` AS `o` +WHERE EXISTS ( + SELECT 1 + FROM `OfferActions` AS `o0` + WHERE `o`.`Id` = `o0`.`OfferId` AND `o0`.`Action` = @__action_0) +"""); + } + } + + protected class IssueContext17794 : DbContext + { + public DbSet Offers { get; set; } + public DbSet OfferActions { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + public IssueContext17794(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + Add( + new Offer17794 { Actions = new List { new() { Action = OfferActions17794.Accepted } } }); + + SaveChanges(); + } + + public static Expression> HasAction17794(OfferActions17794 action) + where T : IOffer17794 + { + Expression> predicate = oa => oa.Action == action; + + return v => v.Actions.AsQueryable().Any(predicate); + } + + public interface IOffer17794 + { + ICollection Actions { get; set; } + } + + public class Offer17794 : IOffer17794 + { + public int Id { get; set; } + + public ICollection Actions { get; set; } + } + + public enum OfferActions17794 + { + Accepted = 1, + Declined = 2 + } + + public class OfferAction17794 + { + public int Id { get; set; } + + [Required] + public Offer17794 Offer { get; set; } + + public int OfferId { get; set; } + + [Required] + public OfferActions17794 Action { get; set; } + } + } + + #endregion + + #region Issue18087 + + [ConditionalFact] + public async Task Casts_are_removed_from_expression_tree_when_redundant() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + // implemented_interface + using (var context = contextFactory.CreateContext()) + { + var queryBase = (IQueryable)context.MockEntities; + var id = 1; + var query = queryBase.Cast().FirstOrDefault(x => x.Id == id); + + Assert.Equal(1, query.Id); + + AssertSql( +""" +@__id_0='1' + +SELECT TOP 1 `m`.`Id`, `m`.`Name`, `m`.`NavigationEntityId` +FROM `MockEntities` AS `m` +WHERE `m`.`Id` = @__id_0 +"""); + } + + // object + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var queryBase = (IQueryable)context.MockEntities; + var query = queryBase.Cast().Count(); + + Assert.Equal(3, query); + + AssertSql( +""" +SELECT COUNT(*) +FROM `MockEntities` AS `m` +"""); + } + + // non_implemented_interface + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var queryBase = (IQueryable)context.MockEntities; + var id = 1; + + var message = Assert.Throws( + () => queryBase.Cast().FirstOrDefault(x => x.Id == id)).Message; + + Assert.Equal( + CoreStrings.TranslationFailed( + @"DbSet() .Cast() .Where(e => e.Id == __id_0)"), + message.Replace("\r", "").Replace("\n", "")); + } + } + + protected class IssueContext18087 : DbContext + { + public IssueContext18087(DbContextOptions options) + : base(options) + { + } + + public DbSet MockEntities { get; set; } + + public void Seed() + { + AddRange( + new MockEntity { Name = "Entity1", NavigationEntity = null }, + new MockEntity { Name = "Entity2", NavigationEntity = null }, + new MockEntity { Name = "NewEntity", NavigationEntity = null }); + + SaveChanges(); + } + + public interface IDomainEntity + { + int Id { get; set; } + } + + public interface IDummyEntity + { + int Id { get; set; } + } + + public class MockEntity : IDomainEntity + { + public int Id { get; set; } + public string Name { get; set; } + + public MockEntity NavigationEntity { get; set; } + } + } + + #endregion + + #region Issue18759 + + [ConditionalFact] + public async Task Query_filter_with_null_constant() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + var people = context.People.ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `p`.`UserDeleteId` +FROM `People` AS `p` +LEFT JOIN `User18759` AS `u` ON `p`.`UserDeleteId` = `u`.`Id` +WHERE `u`.`Id` IS NOT NULL +"""); + } + } + + protected class IssueContext18759 : DbContext + { + public DbSet People { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasQueryFilter(p => p.UserDelete != null); + + public IssueContext18759(DbContextOptions options) + : base(options) + { + } + + public class Person18759 + { + public int Id { get; set; } + public User18759 UserDelete { get; set; } + } + + public class User18759 + { + public int Id { get; set; } + } + } + + #endregion + + #region Issue19138 + + [ConditionalFact] + public async Task Accessing_scalar_property_in_derived_type_projection_does_not_load_owned_navigations() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var result = context.BaseEntities + .Select( + b => context.OtherEntities.Where(o => o.OtherEntityData == ((IssueContext19138.SubEntity19138)b).Data) + .FirstOrDefault()) + .ToList(); + + Assert.Equal("A", Assert.Single(result).OtherEntityData); + + AssertSql( + """ +SELECT [t0].[Id], [t0].[OtherEntityData] +FROM [BaseEntities] AS [b] +LEFT JOIN ( + SELECT [t].[Id], [t].[OtherEntityData] + FROM ( + SELECT [o].[Id], [o].[OtherEntityData], ROW_NUMBER() OVER(PARTITION BY [o].[OtherEntityData] ORDER BY [o].[Id]) AS [row] + FROM [OtherEntities] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [b].[Data] = [t0].[OtherEntityData] +"""); + } + } + + protected class IssueContext19138 : DbContext + { + public DbSet BaseEntities { get; set; } + public DbSet OtherEntities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(se => se.Owned); + modelBuilder.Entity(); + } + + public IssueContext19138(DbContextOptions options) + : base(options) + { + } + + public void Seed() + { + Add(new OtherEntity19138 { OtherEntityData = "A" }); + Add(new SubEntity19138 { Data = "A" }); + + SaveChanges(); + } + + public class BaseEntity19138 + { + public int Id { get; set; } + } + + public class SubEntity19138 : BaseEntity19138 + { + public string Data { get; set; } + public Owned19138 Owned { get; set; } + } + + public class Owned19138 + { + public string OwnedData { get; set; } + public int Value { get; set; } + } + + public class OtherEntity19138 + { + public int Id { get; set; } + public string OtherEntityData { get; set; } + } + } + + #endregion + + #region Issue19708 + + [ConditionalFact] + public async Task GroupJoin_SelectMany_gets_flattened() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = context.CustomerFilters.ToList(); + + AssertSql( +""" +SELECT `c`.`CustomerId`, `c`.`CustomerMembershipId` +FROM `CustomerFilters` AS `c` +WHERE ( + SELECT COUNT(*) + FROM `Customers` AS `c0` + LEFT JOIN `CustomerMemberships` AS `c1` ON `c0`.`Id` = `c1`.`CustomerId` + WHERE `c1`.`Id` IS NOT NULL AND `c0`.`Id` = `c`.`CustomerId`) > 0 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.Set().ToList(); + + Assert.Collection( + query, + t => AssertCustomerView(t, 1, "First", 1, "FirstChild"), + t => AssertCustomerView(t, 2, "Second", 2, "SecondChild1"), + t => AssertCustomerView(t, 2, "Second", 3, "SecondChild2"), + t => AssertCustomerView(t, 3, "Third", null, "")); + + static void AssertCustomerView( + IssueContext19708.CustomerView19708 actual, + int id, + string name, + int? customerMembershipId, + string customerMembershipName) + { + Assert.Equal(id, actual.Id); + Assert.Equal(name, actual.Name); + Assert.Equal(customerMembershipId, actual.CustomerMembershipId); + Assert.Equal(customerMembershipName, actual.CustomerMembershipName); + } + + AssertSql( +""" +SELECT `c`.`Id`, `c`.`Name`, `c0`.`Id` AS `CustomerMembershipId`, IIF(`c0`.`Id` IS NOT NULL, `c0`.`Name`, '') AS `CustomerMembershipName` +FROM `Customers` AS `c` +LEFT JOIN `CustomerMemberships` AS `c0` ON `c`.`Id` = `c0`.`CustomerId` +"""); + } + } + + protected class IssueContext19708 : DbContext + { + public IssueContext19708(DbContextOptions options) + : base(options) + { + } + + public DbSet Customers { get; set; } + public DbSet CustomerMemberships { get; set; } + public DbSet CustomerFilters { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasQueryFilter( + e => (from a in (from c in Customers + join cm in CustomerMemberships on c.Id equals cm.CustomerId into g + from cm in g.DefaultIfEmpty() + select new { c.Id, CustomerMembershipId = (int?)cm.Id }) + where a.CustomerMembershipId != null && a.Id == e.CustomerId + select a).Count() + > 0) + .HasKey(e => e.CustomerId); + +#pragma warning disable CS0618 // Type or member is obsolete + modelBuilder.Entity().HasNoKey().ToQuery(Build_Customers_Sql_View_InMemory()); +#pragma warning restore CS0618 // Type or member is obsolete + } + + public void Seed() + { + var customer1 = new Customer19708 { Name = "First" }; + var customer2 = new Customer19708 { Name = "Second" }; + var customer3 = new Customer19708 { Name = "Third" }; + + var customerMembership1 = new CustomerMembership19708 { Name = "FirstChild", Customer = customer1 }; + var customerMembership2 = new CustomerMembership19708 { Name = "SecondChild1", Customer = customer2 }; + var customerMembership3 = new CustomerMembership19708 { Name = "SecondChild2", Customer = customer2 }; + + AddRange(customer1, customer2, customer3); + AddRange(customerMembership1, customerMembership2, customerMembership3); + + SaveChanges(); + } + + private Expression>> Build_Customers_Sql_View_InMemory() + { + Expression>> query = () => + from customer in Customers + join customerMembership in CustomerMemberships on customer.Id equals customerMembership.CustomerId into + nullableCustomerMemberships + from customerMembership in nullableCustomerMemberships.DefaultIfEmpty() + select new CustomerView19708 + { + Id = customer.Id, + Name = customer.Name, + CustomerMembershipId = customerMembership != null ? customerMembership.Id : default(int?), + CustomerMembershipName = customerMembership != null ? customerMembership.Name : "" + }; + return query; + } + + public class Customer19708 + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class CustomerMembership19708 + { + public int Id { get; set; } + public string Name { get; set; } + + public int CustomerId { get; set; } + public Customer19708 Customer { get; set; } + } + + public class CustomerFilter19708 + { + public int CustomerId { get; set; } + public int CustomerMembershipId { get; set; } + } + + public class CustomerView19708 + { + public int Id { get; set; } + public string Name { get; set; } + public int? CustomerMembershipId { get; set; } + public string CustomerMembershipName { get; set; } + } + } + + #endregion + + #region Issue20097 + + [ConditionalFact] + public async Task Interface_casting_though_generic_method() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var originalQuery = context.Entities.Select(a => new IssueContext20097.MyModel20097 { Id = a.Id }); + var query = IssueContext20097.AddFilter(originalQuery, 1).ToList(); + + Assert.Single(query); + + AssertSql( + """ +@__id_0='1' + +SELECT [e].[Id] +FROM [Entities] AS [e] +WHERE [e].[Id] = @__id_0 +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var originalQuery = context.Entities.Select(a => new IssueContext20097.MyModel20097 { Id = a.Id }); + var query = originalQuery.Where(a => a.Id == 1).ToList(); + + Assert.Single(query); + + AssertSql( + """ +SELECT [e].[Id] +FROM [Entities] AS [e] +WHERE [e].[Id] = CAST(1 AS bigint) +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var originalQuery = context.Entities.Select(a => new IssueContext20097.MyModel20097 { Id = a.Id }); + var query = originalQuery.Where(a => ((IssueContext20097.IHaveId20097)a).Id == 1).ToList(); + + Assert.Single(query); + + AssertSql( + """ +SELECT [e].[Id] +FROM [Entities] AS [e] +WHERE [e].[Id] = CAST(1 AS bigint) +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var originalQuery = context.Entities.Select(a => new IssueContext20097.MyModel20097 { Id = a.Id }); + var query = originalQuery.Where(a => (a as IssueContext20097.IHaveId20097).Id == 1).ToList(); + + Assert.Single(query); + + AssertSql( + """ +SELECT [e].[Id] +FROM [Entities] AS [e] +WHERE [e].[Id] = CAST(1 AS bigint) +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var originalQuery = context.Entities.Select(a => new IssueContext20097.MyModel20097 { Id = a.Id }); + var query = originalQuery.Where(a => ((IssueContext20097.IHaveId20097)a).Id == 1).ToList(); + Assert.Single(query); + + AssertSql( + """ +SELECT [e].[Id] +FROM [Entities] AS [e] +WHERE [e].[Id] = CAST(1 AS bigint) +"""); + } + } + + protected class IssueContext20097 : DbContext + { + public IssueContext20097(DbContextOptions options) + : base(options) + { + } + + public DbSet Entities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + public static IQueryable AddFilter(IQueryable query, long id) + where T : IHaveId20097 + => query.Where(a => a.Id == id); + + public void Seed() + { + Add(new Entity20097()); + + SaveChanges(); + } + + public class Entity20097 + { + public long Id { get; set; } + } + + public interface IHaveId20097 + { + long Id { get; } + } + + public class MyModel20097 : IHaveId20097 + { + public long Id { get; set; } + } + } + + #endregion + + #region Issue20609 + + [ConditionalFact] + public virtual async Task Can_ignore_invalid_include_path_error() + { + var contextFactory = await InitializeAsync( + onConfiguring: o => o.ConfigureWarnings(x => x.Ignore(CoreEventId.InvalidIncludePathError))); + + using var context = contextFactory.CreateContext(); + var result = context.Set().Include("SubB").ToList(); + } + + protected class IssueContext20609 : DbContext + { + public IssueContext20609(DbContextOptions options) + : base(options) + { + } + + public DbSet BaseClasses { get; set; } + public DbSet SubAs { get; set; } + public DbSet SubBs { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasBaseType().HasOne(x => x.SubA).WithMany(); + modelBuilder.Entity().HasBaseType().HasOne(x => x.SubB).WithMany(); + } + + public class BaseClass + { + public string Id { get; set; } + } + + public class ClassA : BaseClass + { + public SubA SubA { get; set; } + } + + public class ClassB : BaseClass + { + public SubB SubB { get; set; } + } + + public class SubA + { + public int Id { get; set; } + } + + public class SubB + { + public int Id { get; set; } + } + } + + #endregion + + #region Issue21355 + + [ConditionalFact] + public virtual async Task Can_configure_SingleQuery_at_context_level() + { + var contextFactory = await InitializeAsync( + seed: c => c.Seed(), + onConfiguring: o => new JetDbContextOptionsBuilder(o).UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)); + + using (var context = contextFactory.CreateContext()) + { + var result = context.Parents.Include(p => p.Children1).ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `c`.`Id`, `c`.`ParentId` +FROM `Parents` AS `p` +LEFT JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var result = context.Parents.Include(p => p.Children1).AsSplitQuery().ToList(); + + AssertSql( +""" +SELECT `p`.`Id` +FROM `Parents` AS `p` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `c`.`Id`, `c`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + context.Parents.Include(p => p.Children1).Include(p => p.Children2).ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `c`.`Id`, `c`.`ParentId`, `a`.`Id`, `a`.`ParentId` +FROM (`Parents` AS `p` +LEFT JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId`) +LEFT JOIN `AnotherChild21355` AS `a` ON `p`.`Id` = `a`.`ParentId` +ORDER BY `p`.`Id`, `c`.`Id` +"""); + } + } + + [ConditionalFact] + public virtual async Task Can_configure_SplitQuery_at_context_level() + { + var contextFactory = await InitializeAsync( + seed: c => c.Seed(), + onConfiguring: o => new JetDbContextOptionsBuilder(o).UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); + + using (var context = contextFactory.CreateContext()) + { + var result = context.Parents.Include(p => p.Children1).ToList(); + + AssertSql( +""" +SELECT `p`.`Id` +FROM `Parents` AS `p` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `c`.`Id`, `c`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var result = context.Parents.Include(p => p.Children1).AsSingleQuery().ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `c`.`Id`, `c`.`ParentId` +FROM `Parents` AS `p` +LEFT JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + context.Parents.Include(p => p.Children1).Include(p => p.Children2).ToList(); + + AssertSql( +""" +SELECT `p`.`Id` +FROM `Parents` AS `p` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `c`.`Id`, `c`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `a`.`Id`, `a`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `AnotherChild21355` AS `a` ON `p`.`Id` = `a`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + } + + [ConditionalFact] + public virtual async Task Unconfigured_query_splitting_behavior_throws_a_warning() + { + var contextFactory = await InitializeAsync( + seed: c => c.Seed(), onConfiguring: o => ClearQuerySplittingBehavior(o)); + + using (var context = contextFactory.CreateContext()) + { + context.Parents.Include(p => p.Children1).Include(p => p.Children2).AsSplitQuery().ToList(); + + AssertSql( +""" +SELECT `p`.`Id` +FROM `Parents` AS `p` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `c`.`Id`, `c`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `a`.`Id`, `a`.`ParentId`, `p`.`Id` +FROM `Parents` AS `p` +INNER JOIN `AnotherChild21355` AS `a` ON `p`.`Id` = `a`.`ParentId` +ORDER BY `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + Assert.Contains( + RelationalResources.LogMultipleCollectionIncludeWarning(new TestLogger()) + .GenerateMessage(), + Assert.Throws( + () => context.Parents.Include(p => p.Children1).Include(p => p.Children2).ToList()).Message); + } + } + + [ConditionalFact] + public virtual async Task Using_AsSingleQuery_without_context_configuration_does_not_throw_warning() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using var context = contextFactory.CreateContext(); + + context.Parents.Include(p => p.Children1).Include(p => p.Children2).AsSingleQuery().ToList(); + + AssertSql( +""" +SELECT `p`.`Id`, `c`.`Id`, `c`.`ParentId`, `a`.`Id`, `a`.`ParentId` +FROM (`Parents` AS `p` +LEFT JOIN `Child21355` AS `c` ON `p`.`Id` = `c`.`ParentId`) +LEFT JOIN `AnotherChild21355` AS `a` ON `p`.`Id` = `a`.`ParentId` +ORDER BY `p`.`Id`, `c`.`Id` +"""); + } + + [ConditionalFact] + public virtual async Task SplitQuery_disposes_inner_data_readers() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + ((RelationalTestStore)contextFactory.TestStore).CloseConnection(); + + using (var context = contextFactory.CreateContext()) + { + context.Parents.Include(p => p.Children1).Include(p => p.Children2).AsSplitQuery().ToList(); + + Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State); + } + + using (var context = contextFactory.CreateContext()) + { + await context.Parents.Include(p => p.Children1).Include(p => p.Children2).AsSplitQuery().ToListAsync(); + + Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State); + } + + using (var context = contextFactory.CreateContext()) + { + context.Parents.Include(p => p.Children1).Include(p => p.Children2).OrderBy(e => e.Id).AsSplitQuery().Single(); + + Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State); + } + + using (var context = contextFactory.CreateContext()) + { + await context.Parents.Include(p => p.Children1).Include(p => p.Children2).OrderBy(e => e.Id).AsSplitQuery().SingleAsync(); + + Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State); + } + } + + [ConditionalFact] + public virtual async Task Using_AsSplitQuery_without_multiple_active_result_sets_works() + { + var contextFactory = await InitializeAsync( + seed: c => c.Seed(), + createTestStore: () => JetTestStore.CreateInitialized(StoreName)); + + using var context = contextFactory.CreateContext(); + + context.Parents.Include(p => p.Children1).Include(p => p.Children2).AsSplitQuery().ToList(); + } + + protected class IssueContext21355 : DbContext + { + public IssueContext21355(DbContextOptions options) + : base(options) + { + } + + public DbSet Parents { get; set; } + + public void Seed() + { + Add(new Parent21355 { Id = "Parent1", Children1 = new List { new(), new() } }); + SaveChanges(); + } + + public class Parent21355 + { + public string Id { get; set; } + public List Children1 { get; set; } + public List Children2 { get; set; } + } + + public class Child21355 + { + public int Id { get; set; } + public string ParentId { get; set; } + public Parent21355 Parent { get; set; } + } + + public class AnotherChild21355 + { + public int Id { get; set; } + public string ParentId { get; set; } + public Parent21355 Parent { get; set; } + } + } + + #endregion + + #region Issue21540 + + [ConditionalFact] + public virtual async Task Can_auto_include_navigation_from_model() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Parents.AsNoTracking().ToList(); + + var result = Assert.Single(query); + Assert.NotNull(result.OwnedReference); + Assert.NotNull(result.Reference); + Assert.NotNull(result.Collection); + Assert.Equal(2, result.Collection.Count); + Assert.NotNull(result.SkipOtherSide); + Assert.Single(result.SkipOtherSide); + + AssertSql( +""" +SELECT `p`.`Id`, `r`.`Id`, `c`.`Id`, `c`.`ParentId`, `p`.`OwnedReference_Id`, `r`.`ParentId`, `t`.`Id`, `t`.`ParentId`, `t`.`OtherSideId` +FROM ((`Parents` AS `p` +LEFT JOIN `Reference21540` AS `r` ON `p`.`Id` = `r`.`ParentId`) +LEFT JOIN `Collection21540` AS `c` ON `p`.`Id` = `c`.`ParentId`) +LEFT JOIN ( + SELECT `o`.`Id`, `j`.`ParentId`, `j`.`OtherSideId` + FROM `JoinEntity21540` AS `j` + INNER JOIN `OtherSide21540` AS `o` ON `j`.`OtherSideId` = `o`.`Id` +) AS `t` ON `p`.`Id` = `t`.`ParentId` +ORDER BY `p`.`Id`, `r`.`Id`, `c`.`Id`, `t`.`ParentId`, `t`.`OtherSideId` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var query = context.Parents.AsNoTracking().IgnoreAutoIncludes().ToList(); + + var result = Assert.Single(query); + Assert.NotNull(result.OwnedReference); + Assert.Null(result.Reference); + Assert.Null(result.Collection); + Assert.Null(result.SkipOtherSide); + + AssertSql( +""" +SELECT `p`.`Id`, `p`.`OwnedReference_Id` +FROM `Parents` AS `p` +"""); + } + } + + protected class MyContext21540 : DbContext + { + public DbSet Parents { get; set; } + + public MyContext21540(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasMany(e => e.SkipOtherSide).WithMany(e => e.SkipParent) + .UsingEntity( + e => e.HasOne(i => i.OtherSide).WithMany().HasForeignKey(e => e.OtherSideId), + e => e.HasOne(i => i.Parent).WithMany().HasForeignKey(e => e.ParentId)) + .HasKey(e => new { e.ParentId, e.OtherSideId }); + modelBuilder.Entity().OwnsOne(e => e.OwnedReference); + + modelBuilder.Entity().Navigation(e => e.Reference).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Collection).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.SkipOtherSide).AutoInclude(); + } + + public void Seed() + { + var joinEntity = new JoinEntity21540 + { + OtherSide = new OtherSide21540(), + Parent = new Parent21540 + { + Reference = new Reference21540(), + OwnedReference = new Owned21540(), + Collection = new List + { + new(), new(), + } + } + }; + + AddRange(joinEntity); + + SaveChanges(); + } + + public class Parent21540 + { + public int Id { get; set; } + public Reference21540 Reference { get; set; } + public Owned21540 OwnedReference { get; set; } + public List Collection { get; set; } + public List SkipOtherSide { get; set; } + } + + public class JoinEntity21540 + { + public int ParentId { get; set; } + public Parent21540 Parent { get; set; } + public int OtherSideId { get; set; } + public OtherSide21540 OtherSide { get; set; } + } + + public class OtherSide21540 + { + public int Id { get; set; } + public List SkipParent { get; set; } + } + + public class Reference21540 + { + public int Id { get; set; } + public int ParentId { get; set; } + public Parent21540 Parent { get; set; } + } + + public class Owned21540 + { + public int Id { get; set; } + } + + public class Collection21540 + { + public int Id { get; set; } + public int ParentId { get; set; } + public Parent21540 Parent { get; set; } + } + } + + #endregion + + #region Issue18346 + + [ConditionalFact] + public virtual async Task Can_query_hierarchy_with_non_nullable_property_on_derived() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Businesses.ToList(); + Assert.Equal(3, query.Count); + + AssertSql( +""" +SELECT `b`.`Id`, `b`.`Name`, `b`.`Type`, `b`.`IsOnline` +FROM `Businesses` AS `b` +"""); + } + } + + protected class MyContext18346 : DbContext + { + public DbSet Businesses { get; set; } + + public MyContext18346(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity() + .HasDiscriminator(x => x.Type) + .HasValue(BusinessType18346.Shop) + .HasValue(BusinessType18346.Brand); + + public void Seed() + { + var shop1 = new Shop18346 { IsOnline = true, Name = "Amzn" }; + var shop2 = new Shop18346 { IsOnline = false, Name = "Mom and Pop's Shoppe" }; + var brand = new Brand18346 { Name = "Tsla" }; + Businesses.AddRange(shop1, shop2, brand); + SaveChanges(); + } + + public abstract class Business18346 + { + public int Id { get; set; } + public string Name { get; set; } + public BusinessType18346 Type { get; set; } + } + + public class Shop18346 : Business18346 + { + public bool IsOnline { get; set; } + } + + public class Brand18346 : Business18346 + { + } + + public enum BusinessType18346 + { + Shop, + Brand, + } + } + + #endregion + + #region Issue21666 + + [ConditionalFact] + public virtual async Task Thread_safety_in_relational_command_cache() + { + var contextFactory = await InitializeAsync( + onConfiguring: options => ((IDbContextOptionsBuilderInfrastructure)options).AddOrUpdateExtension( + options.Options.FindExtension() + .WithConnection(null) + .WithConnectionString(JetTestStore.CreateConnectionString(StoreName)))); + + var ids = new[] { 1, 2, 3 }; + + Parallel.For( + 0, 100, + i => + { + using var context = contextFactory.CreateContext(); + var query = context.Lists.Where(l => !l.IsDeleted && ids.Contains(l.Id)).ToList(); + }); + } + + protected class MyContext21666 : DbContext + { + public DbSet Lists { get; set; } + + public MyContext21666(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + public class List21666 + { + public int Id { get; set; } + public bool IsDeleted { get; set; } + } + } + + #endregion + + #region Issue21768 + + [ConditionalFact] + public virtual async Task Using_explicit_interface_implementation_as_navigation_works() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + Expression> projection = + b => new MyContext21768.BookViewModel21768 + { + FirstPage = b.FrontCover.Illustrations.FirstOrDefault( + i => i.State >= MyContext21768.IllustrationState21768.Approved) + != null + ? new MyContext21768.PageViewModel21768 + { + Uri = b.FrontCover.Illustrations + .FirstOrDefault(i => i.State >= MyContext21768.IllustrationState21768.Approved).Uri + } + : null, + }; + + var result = context.Books.Where(b => b.Id == 1).Select(projection).SingleOrDefault(); + + AssertSql( +""" +SELECT TOP 2 IIF(EXISTS ( + SELECT 1 + FROM `CoverIllustrations` AS `c` + WHERE `b0`.`Id` = `c`.`CoverId` AND `c`.`State` >= 2), TRUE, FALSE), ( + SELECT TOP 1 `c0`.`Uri` + FROM `CoverIllustrations` AS `c0` + WHERE `b0`.`Id` = `c0`.`CoverId` AND `c0`.`State` >= 2) +FROM `Books` AS `b` +INNER JOIN `BookCovers` AS `b0` ON `b`.`FrontCoverId` = `b0`.`Id` +WHERE `b`.`Id` = 1 +"""); + } + } + + protected class MyContext21768 : DbContext + { + public DbSet Books { get; set; } + public DbSet BookCovers { get; set; } + public DbSet CoverIllustrations { get; set; } + + public MyContext21768(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + foreach (var fk in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) + { + fk.DeleteBehavior = DeleteBehavior.NoAction; + } + } + + public class BookViewModel21768 + { + public PageViewModel21768 FirstPage { get; set; } + } + + public class PageViewModel21768 + { + public string Uri { get; set; } + } + + public interface IBook21768 + { + public int Id { get; set; } + + public IBookCover21768 FrontCover { get; } + public int FrontCoverId { get; set; } + + public IBookCover21768 BackCover { get; } + public int BackCoverId { get; set; } + } + + public interface IBookCover21768 + { + public int Id { get; set; } + public IEnumerable Illustrations { get; } + } + + public interface ICoverIllustration21768 + { + public int Id { get; set; } + public IBookCover21768 Cover { get; } + public int CoverId { get; set; } + public string Uri { get; set; } + public IllustrationState21768 State { get; set; } + } + + public class Book21768 : IBook21768 + { + public int Id { get; set; } + + public BookCover21768 FrontCover { get; set; } + public int FrontCoverId { get; set; } + + public BookCover21768 BackCover { get; set; } + public int BackCoverId { get; set; } + + IBookCover21768 IBook21768.FrontCover + => FrontCover; + + IBookCover21768 IBook21768.BackCover + => BackCover; + } + + public class BookCover21768 : IBookCover21768 + { + public int Id { get; set; } + public ICollection Illustrations { get; set; } + + IEnumerable IBookCover21768.Illustrations + => Illustrations; + } + + public class CoverIllustration21768 : ICoverIllustration21768 + { + public int Id { get; set; } + public BookCover21768 Cover { get; set; } + public int CoverId { get; set; } + public string Uri { get; set; } + public IllustrationState21768 State { get; set; } + + IBookCover21768 ICoverIllustration21768.Cover + => Cover; + } + + public enum IllustrationState21768 + { + New, + PendingApproval, + Approved, + Printed + } + } + + #endregion + + #region Issue19206 + + [ConditionalFact] + public virtual async Task From_sql_expression_compares_correctly() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + var query = from t1 in context.Tests.FromSqlInterpolated( + $"Select * from Tests Where Type = {MyContext19206.TestType19206.Unit}") + from t2 in context.Tests.FromSqlInterpolated( + $"Select * from Tests Where Type = {MyContext19206.TestType19206.Integration}") + select new { t1, t2 }; + + var result = query.ToList(); + + var item = Assert.Single(result); + Assert.Equal(MyContext19206.TestType19206.Unit, item.t1.Type); + Assert.Equal(MyContext19206.TestType19206.Integration, item.t2.Type); + + AssertSql( +""" +p0='0' +p1='1' + +SELECT `e`.`Id`, `e`.`Type`, `e0`.`Id`, `e0`.`Type` +FROM ( + Select * from Tests Where Type = @p0 +) AS `e`, +( + Select * from Tests Where Type = @p1 +) AS `e0` +"""); + } + } + + protected class MyContext19206 : DbContext + { + public DbSet Tests { get; set; } + + public MyContext19206(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + public void Seed() + { + Add(new Test19206 { Type = TestType19206.Unit }); + Add(new Test19206 { Type = TestType19206.Integration }); + SaveChanges(); + } + + public class Test19206 + { + public int Id { get; set; } + public TestType19206 Type { get; set; } + } + + public enum TestType19206 + { + Unit, + Integration, + } + } + + #endregion + + #region Issue18510 + + [ConditionalFact] + public virtual async Task Invoke_inside_query_filter_gets_correctly_evaluated_during_translation() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + context.TenantId = 1; + + var query1 = context.Entities.ToList(); + Assert.True(query1.All(x => x.TenantId == 1)); + + context.TenantId = 2; + var query2 = context.Entities.ToList(); + Assert.True(query2.All(x => x.TenantId == 2)); + + AssertSql( +""" +@__ef_filter__p_0='1' + +SELECT `e`.`Id`, `e`.`Name`, `e`.`TenantId` +FROM `Entities` AS `e` +WHERE (`e`.`Name` <> 'Foo' OR `e`.`Name` IS NULL) AND `e`.`TenantId` = @__ef_filter__p_0 +""", +// +""" +@__ef_filter__p_0='2' + +SELECT `e`.`Id`, `e`.`Name`, `e`.`TenantId` +FROM `Entities` AS `e` +WHERE (`e`.`Name` <> 'Foo' OR `e`.`Name` IS NULL) AND `e`.`TenantId` = @__ef_filter__p_0 +"""); + } + } + + protected class MyContext18510 : DbContext { - return type.GetTypeInfo().GetProperty(name); + public DbSet Entities { get; set; } + + public int TenantId { get; set; } + + public MyContext18510(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasQueryFilter(x => x.Name != "Foo"); + + var entityType = modelBuilder.Model.GetEntityTypes().Single(et => et.ClrType == typeof(MyEntity18510)); + var queryFilter = entityType.GetQueryFilter(); + Expression> tenantFunc = () => TenantId; + var tenant = Expression.Invoke(tenantFunc); + + var efPropertyMethod = typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(EF.Property)).MakeGenericMethod(typeof(int)); + var prm = queryFilter.Parameters[0]; + var efPropertyMethodCall = Expression.Call(efPropertyMethod, prm, Expression.Constant("TenantId")); + + var updatedQueryFilter = Expression.Lambda( + Expression.AndAlso( + queryFilter.Body, + Expression.Equal( + efPropertyMethodCall, + tenant)), + prm); + + entityType.SetQueryFilter(updatedQueryFilter); + } + + public void Seed() + { + var e1 = new MyEntity18510 { Name = "e1", TenantId = 1 }; + var e2 = new MyEntity18510 { Name = "e2", TenantId = 2 }; + var e3 = new MyEntity18510 { Name = "e3", TenantId = 2 }; + var e4 = new MyEntity18510 { Name = "Foo", TenantId = 2 }; + + Entities.AddRange(e1, e2, e3, e4); + SaveChanges(); + } + + public class MyEntity18510 + { + public int Id { get; set; } + public string Name { get; set; } + + public int TenantId { get; set; } + } } - [ConditionalFact] - public virtual void Null_check_removal_applied_recursively() + #endregion + + #region Issue21803 + + [ConditionalTheory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public virtual async Task Select_enumerable_navigation_backed_by_collection(bool async, bool split) { - using (CreateDatabase15204()) - { - var userParam = Expression.Parameter(typeof(TBuilding15204), "s"); - var builderProperty = Expression.MakeMemberAccess(userParam, GetMemberInfo(typeof(TBuilding15204), "Builder")); - var cityProperty = Expression.MakeMemberAccess(builderProperty, GetMemberInfo(typeof(TBuilder15204), "City")); - var nameProperty = Expression.MakeMemberAccess(cityProperty, GetMemberInfo(typeof(TCity15204), "Name")); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - //{s => (IIF((IIF((s.Builder == null), null, s.Builder.City) == null), null, s.Builder.City.Name) == "Leeds")} - var selection = Expression.Lambda>( - Expression.Equal( - Expression.Condition( - Expression.Equal( - Expression.Condition( - Expression.Equal( - builderProperty, - Expression.Constant(null, typeof(TBuilder15204))), - Expression.Constant(null, typeof(TCity15204)), - cityProperty), - Expression.Constant(null, typeof(TCity15204))), - Expression.Constant(null, typeof(string)), - nameProperty), - Expression.Constant("Leeds", typeof(string))), - userParam); + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Select(appEntity => appEntity.OtherEntities); - using (var context = new MyContext15204(_options)) + if (split) { - var query = context.BuildingSet - .Where(selection) - .Include(a => a.Builder).ThenInclude(a => a.City) - .Include(a => a.Mandator).ToList(); + query = query.AsSplitQuery(); + } - Assert.True(query.Count == 1); - Assert.True(query.First().Builder.City.Name == "Leeds"); - Assert.True(query.First().LongName == "Two L2"); + if (async) + { + await query.ToListAsync(); + } + else + { + query.ToList(); + } + if (split) + { AssertSql( - $@"SELECT `b`.`Id`, `b`.`BuilderId`, `b`.`Identity`, `b`.`LongName`, `b`.`MandatorId`, `b0`.`Id`, `b0`.`CityId`, `b0`.`Name`, `c`.`Id`, `c`.`Name`, `m`.`Id`, `m`.`Identity`, `m`.`Name` -FROM `BuildingSet` AS `b` -INNER JOIN `Builder` AS `b0` ON `b`.`BuilderId` = `b0`.`Id` -INNER JOIN `City` AS `c` ON `b0`.`CityId` = `c`.`Id` -INNER JOIN `MandatorSet` AS `m` ON `b`.`MandatorId` = `m`.`Id` -WHERE `c`.`Name` = 'Leeds'"); +""" +SELECT `e`.`Id` +FROM `Entities` AS `e` +ORDER BY `e`.`Id` +""", +// +""" +SELECT `o`.`Id`, `o`.`AppEntityId`, `e`.`Id` +FROM `Entities` AS `e` +INNER JOIN `OtherEntity21803` AS `o` ON `e`.`Id` = `o`.`AppEntityId` +ORDER BY `e`.`Id` +"""); + } + else + { + AssertSql( +""" +SELECT `e`.`Id`, `o`.`Id`, `o`.`AppEntityId` +FROM `Entities` AS `e` +LEFT JOIN `OtherEntity21803` AS `o` ON `e`.`Id` = `o`.`AppEntityId` +ORDER BY `e`.`Id` +"""); } } } - private JetTestStore CreateDatabase15204() - => CreateTestStore( - () => new MyContext15204(_options), - context => - { - var london = new TCity15204 { Name = "London" }; - var sam = new TBuilder15204 { Name = "Sam", City = london }; - - context.MandatorSet.Add( - new TMandator15204 - { - Identity = Guid.NewGuid(), - Name = "One", - Buildings = new List - { - new TBuilding15204 - { - Identity = Guid.NewGuid(), - LongName = "One L1", - Builder = sam - }, - new TBuilding15204 - { - Identity = Guid.NewGuid(), - LongName = "One L2", - Builder = sam - } - } - }); - context.MandatorSet.Add( - new TMandator15204 - { - Identity = Guid.NewGuid(), - Name = "Two", - Buildings = new List - { - new TBuilding15204 - { - Identity = Guid.NewGuid(), - LongName = "Two L1", - Builder = new TBuilder15204 { Name = "John", City = london } - }, - new TBuilding15204 - { - Identity = Guid.NewGuid(), - LongName = "Two L2", - Builder = new TBuilder15204 { Name = "Mark", City = new TCity15204 { Name = "Leeds" } } - } - } - }); - - context.SaveChanges(); - - ClearLog(); - }); - - private class MyContext15204 : DbContext + protected class MyContext21803 : DbContext { - public DbSet MandatorSet { get; set; } - public DbSet BuildingSet { get; set; } - public DbSet Builder { get; set; } - public DbSet City { get; set; } + public DbSet Entities { get; set; } - public MyContext15204(DbContextOptions options) + public MyContext21803(DbContextOptions options) : base(options) { - ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - ChangeTracker.AutoDetectChangesEnabled = false; } - } - private class TBuilding15204 - { - public int Id { get; set; } - public Guid Identity { get; set; } - public string LongName { get; set; } - public int BuilderId { get; set; } - public TBuilder15204 Builder { get; set; } - public TMandator15204 Mandator { get; set; } - public int MandatorId { get; set; } - } + public void Seed() + { + var appEntity = new AppEntity21803(); + AddRange( + new OtherEntity21803 { AppEntity = appEntity }, + new OtherEntity21803 { AppEntity = appEntity }, + new OtherEntity21803 { AppEntity = appEntity }, + new OtherEntity21803 { AppEntity = appEntity }); - private class TBuilder15204 - { - public int Id { get; set; } - public string Name { get; set; } - public int CityId { get; set; } - public TCity15204 City { get; set; } - } + SaveChanges(); + } - private class TCity15204 - { - public int Id { get; set; } - public string Name { get; set; } - } + public class AppEntity21803 + { + private readonly List _otherEntities = new(); - private class TMandator15204 - { - public int Id { get; set; } - public Guid Identity { get; set; } - public string Name { get; set; } - public virtual ICollection Buildings { get; set; } + public int Id { get; private set; } + + public IEnumerable OtherEntities + => _otherEntities; + } + + public class OtherEntity21803 + { + public int Id { get; private set; } + public AppEntity21803 AppEntity { get; set; } + } } #endregion - #region Bug15518 + #region Issue21807 - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public virtual void Nested_queries_does_not_cause_concurrency_exception_sync(bool tracking) + [ConditionalFact] + public virtual async Task Nested_owned_required_dependents_are_materialized() { - using (CreateDatabase15518()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext15518(_options)) - { - var query = context.Repos.OrderBy(r => r.Id).Where(r => r.Id > 0); - query = tracking ? query.AsTracking() : query.AsNoTracking(); + var query = context.Set().ToList(); - foreach (var a in query) - { - foreach (var b in query) - { - } - } - } + var result = Assert.Single(query); + Assert.NotNull(result.Contact); + Assert.NotNull(result.Contact.Address); + Assert.Equal(12345, result.Contact.Address.Zip); + + AssertSql( +""" +SELECT `e`.`Id`, `e`.`Contact_Name`, `e`.`Contact_Address_City`, `e`.`Contact_Address_State`, `e`.`Contact_Address_Street`, `e`.`Contact_Address_Zip` +FROM `Entity21807` AS `e` +"""); } } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public virtual async Task Nested_queries_does_not_cause_concurrency_exception_async(bool tracking) + protected class MyContext21807 : DbContext { - using (CreateDatabase15518()) + public MyContext21807(DbContextOptions options) + : base(options) { - using (var context = new MyContext15518(_options)) - { - var query = context.Repos.OrderBy(r => r.Id).Where(r => r.Id > 0); - query = tracking ? query.AsTracking() : query.AsNoTracking(); + } - await foreach (var a in query.AsAsyncEnumerable()) + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity( + builder => { - await foreach (var b in query.AsAsyncEnumerable()) - { - } - } - } - } - } + builder.HasKey(x => x.Id); - private JetTestStore CreateDatabase15518() - => CreateTestStore( - () => new MyContext15518(_options), - context => - { - context.AddRange( - new Repo15518 { Name = "London" }, - new Repo15518 { Name = "New York" }); + builder.OwnsOne( + x => x.Contact, contact => + { + contact.OwnsOne(c => c.Address); + }); - context.SaveChanges(); + builder.Navigation(x => x.Contact).IsRequired(); + }); - ClearLog(); - }); + public void Seed() + { + Add(new Entity21807 { Id = "1", Contact = new Contact21807 { Address = new Address21807 { Zip = 12345 } } }); - private class MyContext15518 : DbContext - { - public DbSet Repos { get; set; } + SaveChanges(); + } - public MyContext15518(DbContextOptions options) - : base(options) + public class Entity21807 { + public string Id { get; set; } + public Contact21807 Contact { get; set; } } - } - private class Repo15518 - { - public int Id { get; set; } - public string Name { get; set; } + public class Contact21807 + { + public string Name { get; set; } + public Address21807 Address { get; set; } + } + + public class Address21807 + { + public string Street { get; set; } + public string City { get; set; } + public string State { get; set; } + public int Zip { get; set; } + } } #endregion - #region Bug8864 + #region Issue22054 [ConditionalFact] - public virtual void Select_nested_projection() + public virtual async Task Optional_dependent_is_null_when_sharing_required_column_with_principal() { - using (CreateDatabase8864()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext8864(_options)) - { - var customers = context.Customers - .Select(c => new { Customer = c, CustomerAgain = Get(context, c.Id) }) - .ToList(); + var query = context.Set().OrderByDescending(e => e.Id).ToList(); - Assert.Equal(2, customers.Count); + Assert.Equal(3, query.Count); - foreach (var customer in customers) - { - Assert.Same(customer.Customer, customer.CustomerAgain); - } - } + Assert.Null(query[0].Contact); + Assert.Null(query[0].Data); + Assert.NotNull(query[1].Data); + Assert.NotNull(query[1].Contact); + Assert.Null(query[1].Contact.Address); + Assert.NotNull(query[2].Data); + Assert.NotNull(query[2].Contact); + Assert.NotNull(query[2].Contact.Address); + + AssertSql( + """ +SELECT [u].[Id], [u].[RowVersion], [u].[Contact_MobileNumber], [u].[SharedProperty], [u].[Contact_Address_City], [u].[Contact_Address_Zip], [u].[Data_Data], [u].[Data_Exists], [u].[RowVersion] +FROM [User22054] AS [u] +ORDER BY [u].[Id] DESC +"""); } } - private static Customer8864 Get(MyContext8864 context, int id) - => context.Customers.Single(c => c.Id == id); + protected class MyContext22054 : DbContext + { + public MyContext22054(DbContextOptions options) + : base(options) + { + } - private JetTestStore CreateDatabase8864() - => CreateTestStore( - () => new MyContext8864(_options), - context => - { - context.AddRange( - new Customer8864 { Name = "Alan" }, - new Customer8864 { Name = "Elon" }); + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity( + builder => + { + builder.HasKey(x => x.Id); - context.SaveChanges(); + builder.OwnsOne( + x => x.Contact, contact => + { + contact.Property(e => e.SharedProperty).IsRequired().HasColumnName("SharedProperty"); - ClearLog(); - }); + contact.OwnsOne( + c => c.Address, address => + { + address.Property("SharedProperty").IsRequired().HasColumnName("SharedProperty"); + }); + }); - private class MyContext8864 : DbContext - { - public DbSet Customers { get; set; } + builder.OwnsOne(e => e.Data) + .Property("RowVersion") + .IsRowVersion() + .IsRequired() + .HasColumnType("TIMESTAMP") + .HasColumnName("RowVersion"); + + builder.Property(x => x.RowVersion) + .HasColumnType("TIMESTAMP") + .IsRowVersion() + .IsRequired() + .HasColumnName("RowVersion"); + }); - public MyContext8864(DbContextOptions options) - : base(options) + public void Seed() { + AddRange( + new User22054 + { + Data = new Data22054 { Data = "Data1" }, + Contact = new Contact22054 + { + MobileNumber = "123456", + SharedProperty = "Value1", + Address = new Address22054 + { + City = "Seattle", + Zip = 12345, + SharedProperty = "Value1" + } + } + }, + new User22054 + { + Data = new Data22054 { Data = "Data2" }, + Contact = new Contact22054 + { + MobileNumber = "654321", + SharedProperty = "Value2", + Address = null + } + }, + new User22054 { Contact = null, Data = null }); + + SaveChanges(); } - } - private class Customer8864 - { - public int Id { get; set; } - public string Name { get; set; } + public class User22054 + { + public int Id { get; set; } + public Data22054 Data { get; set; } + public Contact22054 Contact { get; set; } + public byte[] RowVersion { get; set; } + } + + public class Data22054 + { + public string Data { get; set; } + public bool Exists { get; set; } + } + + public class Contact22054 + { + public string MobileNumber { get; set; } + public string SharedProperty { get; set; } + public Address22054 Address { get; set; } + } + + public class Address22054 + { + public string City { get; set; } + public string SharedProperty { get; set; } + public int Zip { get; set; } + } } #endregion - #region Bug7983 + #region Issue14911 [ConditionalFact] - public virtual void New_instances_in_projection_are_not_shared_across_results() + public virtual async Task Owned_entity_multiple_level_in_aggregate() { - using (CreateDatabase7983()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext7983(_options)) - { - var list = context.Posts.Select(p => new PostDTO7983().From(p)).ToList(); + var aggregate = context.Set().OrderByDescending(e => e.Id).FirstOrDefault(); - Assert.Equal(3, list.Count); - Assert.Equal(new[] { "First", "Second", "Third" }, list.Select(dto => dto.Title)); + Assert.Equal(10, aggregate.FirstValueObject.SecondValueObjects[0].FourthValueObject.FifthValueObjects[0].AnyValue); + Assert.Equal( + 20, + aggregate.FirstValueObject.SecondValueObjects[0].ThirdValueObjects[0].FourthValueObject.FifthValueObjects[0].AnyValue); - AssertSql( - $@"SELECT `p`.`Id`, `p`.`BlogId`, `p`.`Title` -FROM `Posts` AS `p`"); - } + AssertSql( +""" +SELECT `t`.`Id`, `t`.`FirstValueObject_Value`, `t2`.`Id`, `t2`.`AggregateId`, `t2`.`FourthValueObject_Value`, `t2`.`Id0`, `t2`.`AnyValue`, `t2`.`SecondValueObjectId`, `t2`.`Id1`, `t2`.`SecondValueObjectId0`, `t2`.`FourthValueObject_Value0`, `t2`.`Id00`, `t2`.`AnyValue0`, `t2`.`ThirdValueObjectId` +FROM ( + SELECT TOP 1 `a`.`Id`, `a`.`FirstValueObject_Value` + FROM `Aggregates` AS `a` + ORDER BY `a`.`Id` DESC +) AS `t` +LEFT JOIN ( + SELECT `s`.`Id`, `s`.`AggregateId`, `s`.`FourthValueObject_Value`, `f`.`Id` AS `Id0`, `f`.`AnyValue`, `f`.`SecondValueObjectId`, `t1`.`Id` AS `Id1`, `t1`.`SecondValueObjectId` AS `SecondValueObjectId0`, `t1`.`FourthValueObject_Value` AS `FourthValueObject_Value0`, `t1`.`Id0` AS `Id00`, `t1`.`AnyValue` AS `AnyValue0`, `t1`.`ThirdValueObjectId` + FROM (`SecondValueObjects` AS `s` + LEFT JOIN `FourthFifthValueObjects` AS `f` ON IIF(`s`.`FourthValueObject_Value` IS NOT NULL, `s`.`Id`, NULL) = `f`.`SecondValueObjectId`) + LEFT JOIN ( + SELECT `t0`.`Id`, `t0`.`SecondValueObjectId`, `t0`.`FourthValueObject_Value`, `t3`.`Id` AS `Id0`, `t3`.`AnyValue`, `t3`.`ThirdValueObjectId` + FROM `ThirdValueObjects` AS `t0` + LEFT JOIN `ThirdFifthValueObjects` AS `t3` ON IIF(`t0`.`FourthValueObject_Value` IS NOT NULL, `t0`.`Id`, NULL) = `t3`.`ThirdValueObjectId` + ) AS `t1` ON `s`.`Id` = `t1`.`SecondValueObjectId` +) AS `t2` ON IIF(`t`.`FirstValueObject_Value` IS NOT NULL, `t`.`Id`, NULL) = `t2`.`AggregateId` +ORDER BY `t`.`Id` DESC, `t2`.`Id`, `t2`.`Id0`, `t2`.`Id1` +"""); } } - private JetTestStore CreateDatabase7983() - => CreateTestStore( - () => new MyContext7983(_options), - context => + protected class MyContext14911 : DbContext + { + public MyContext14911(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity( + builder => + { + builder.ToTable("Aggregates"); + builder.HasKey(e => e.Id); + + builder.OwnsOne( + e => e.FirstValueObject, dr => + { + dr.OwnsMany( + d => d.SecondValueObjects, c => + { + c.ToTable("SecondValueObjects"); + c.Property("Id").IsRequired(); + c.HasKey("Id"); + c.OwnsOne( + b => b.FourthValueObject, b => + { + b.OwnsMany( + t => t.FifthValueObjects, sp => + { + sp.ToTable("FourthFifthValueObjects"); + sp.Property("Id").IsRequired(); + sp.HasKey("Id"); + sp.Property(e => e.AnyValue).IsRequired(); + sp.WithOwner().HasForeignKey("SecondValueObjectId"); + }); + }); + c.OwnsMany( + b => b.ThirdValueObjects, b => + { + b.ToTable("ThirdValueObjects"); + b.Property("Id").IsRequired(); + b.HasKey("Id"); + + b.OwnsOne( + d => d.FourthValueObject, dpd => + { + dpd.OwnsMany( + d => d.FifthValueObjects, sp => + { + sp.ToTable("ThirdFifthValueObjects"); + sp.Property("Id").IsRequired(); + sp.HasKey("Id"); + sp.Property(e => e.AnyValue).IsRequired(); + sp.WithOwner().HasForeignKey("ThirdValueObjectId"); + }); + }); + b.WithOwner().HasForeignKey("SecondValueObjectId"); + }); + c.WithOwner().HasForeignKey("AggregateId"); + }); + }); + }); + + public void Seed() + { + var aggregate = new Aggregate14911 { - context.Add( - new Blog7983 + FirstValueObject = new FirstValueObject14911 + { + SecondValueObjects = new List + { + new() { - Posts = new List + FourthValueObject = + new FourthValueObject14911 + { + FifthValueObjects = new List { new() { AnyValue = 10 } } + }, + ThirdValueObjects = new List { - new Post7983 { Title = "First" }, - new Post7983 { Title = "Second" }, - new Post7983 { Title = "Third" } + new() + { + FourthValueObject = new FourthValueObject14911 + { + FifthValueObjects = new List { new() { AnyValue = 20 } } + } + } } - }); + } + } + } + }; - context.SaveChanges(); + Set().Add(aggregate); - ClearLog(); - }); + SaveChanges(); + } - private class MyContext7983 : DbContext - { - public DbSet Blogs { get; set; } - public DbSet Posts { get; set; } + public class Aggregate14911 + { + public int Id { get; set; } + public FirstValueObject14911 FirstValueObject { get; set; } + } + + public class FirstValueObject14911 + { + public int Value { get; set; } + public List SecondValueObjects { get; set; } + } + + public class SecondValueObject14911 + { + public FourthValueObject14911 FourthValueObject { get; set; } + public List ThirdValueObjects { get; set; } + } + + public class ThirdValueObject14911 + { + public FourthValueObject14911 FourthValueObject { get; set; } + } - public MyContext7983(DbContextOptions options) - : base(options) + public class FourthValueObject14911 + { + public int Value { get; set; } + public List FifthValueObjects { get; set; } + } + + public class FifthValueObject14911 { + public int AnyValue { get; set; } } } - private class Blog7983 - { - public int Id { get; set; } - public string Title { get; set; } + #endregion - public ICollection Posts { get; set; } - } + #region Issue15215 - private class Post7983 + [ConditionalFact] + public virtual async Task Repeated_parameters_in_generated_query_sql() { - public int Id { get; set; } - public string Title { get; set; } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - public int? BlogId { get; set; } - public Blog7983 Blog { get; set; } - } + using (var context = contextFactory.CreateContext()) + { + var k = 1; + var a = context.Autos.Where(e => e.Id == k).First(); + var b = context.Autos.Where(e => e.Id == k + 1).First(); - private class PostDTO7983 - { - public string Title { get; set; } + var equalQuery = (from d in context.EqualAutos + where (d.Auto == a && d.AnotherAuto == b) + || (d.Auto == b && d.AnotherAuto == a) + select d).ToList(); + + Assert.Single(equalQuery); - public PostDTO7983 From(Post7983 post) + AssertSql( +""" +@__k_0='1' + +SELECT TOP 1 `a`.`Id`, `a`.`Name` +FROM `Autos` AS `a` +WHERE `a`.`Id` = @__k_0 +""", +// +""" +@__p_0='2' + +SELECT TOP 1 `a`.`Id`, `a`.`Name` +FROM `Autos` AS `a` +WHERE `a`.`Id` = @__p_0 +""", +// +""" +@__entity_equality_a_0_Id='1' (Nullable = true) +@__entity_equality_b_1_Id='2' (Nullable = true) +@__entity_equality_b_1_Id='2' (Nullable = true) +@__entity_equality_a_0_Id='1' (Nullable = true) + +SELECT `e`.`Id`, `e`.`AnotherAutoId`, `e`.`AutoId` +FROM (`EqualAutos` AS `e` +LEFT JOIN `Autos` AS `a` ON `e`.`AutoId` = `a`.`Id`) +LEFT JOIN `Autos` AS `a0` ON `e`.`AnotherAutoId` = `a0`.`Id` +WHERE (`a`.`Id` = @__entity_equality_a_0_Id AND `a0`.`Id` = @__entity_equality_b_1_Id) OR (`a`.`Id` = @__entity_equality_b_1_Id AND `a0`.`Id` = @__entity_equality_a_0_Id) +"""); + } + } + + protected class MyContext15215 : DbContext + { + public MyContext15215(DbContextOptions options) + : base(options) { - Title = post.Title; - return this; } - } - - #endregion - #region Bug17253 + public DbSet Autos { get; set; } + public DbSet EqualAutos { get; set; } - [ConditionalFact] - public virtual void Self_reference_in_query_filter_works() - { - using (CreateDatabase17253()) + public void Seed() { - using (var context = new MyContext17253(_options)) + for (var i = 0; i < 10; i++) { - var query = context.EntitiesWithQueryFilterSelfReference.Where(e => e.Name != "Foo"); - var result = query.ToList(); - - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Name` -FROM `EntitiesWithQueryFilterSelfReference` AS `e` -WHERE EXISTS ( - SELECT 1 - FROM `EntitiesWithQueryFilterSelfReference` AS `e0`) AND ((`e`.`Name` <> 'Foo') OR `e`.`Name` IS NULL)"); + Add(new Auto15215 { Name = "Auto " + i }); } + + SaveChanges(); + + AddRange( + new EqualAuto15215 { Auto = Autos.Find(1), AnotherAuto = Autos.Find(2) }, + new EqualAuto15215 { Auto = Autos.Find(5), AnotherAuto = Autos.Find(4) }); + + SaveChanges(); } - } - [ConditionalFact] - public virtual void Self_reference_in_query_filter_works_when_nested() - { - using (CreateDatabase17253()) + public class Auto15215 { - using (var context = new MyContext17253(_options)) - { - var query = context.EntitiesReferencingEntityWithQueryFilterSelfReference.Where(e => e.Name != "Foo"); - var result = query.ToList(); + public int Id { get; set; } + public string Name { get; set; } + } - AssertSql( - $@"SELECT `e`.`Id`, `e`.`Name` -FROM `EntitiesReferencingEntityWithQueryFilterSelfReference` AS `e` -WHERE EXISTS ( - SELECT 1 - FROM `EntitiesWithQueryFilterSelfReference` AS `e0` - WHERE EXISTS ( - SELECT 1 - FROM `EntitiesWithQueryFilterSelfReference` AS `e1`)) AND ((`e`.`Name` <> 'Foo') OR `e`.`Name` IS NULL)"); - } + public class EqualAuto15215 + { + public int Id { get; set; } + public Auto15215 Auto { get; set; } + public Auto15215 AnotherAuto { get; set; } } } - private class MyContext17253 : DbContext + #endregion + + #region Issue22340 + + [ConditionalFact] + public virtual async Task Owned_entity_mapped_to_separate_table() { - public DbSet EntitiesWithQueryFilterSelfReference { get; set; } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - public DbSet EntitiesReferencingEntityWithQueryFilterSelfReference + using (var context = contextFactory.CreateContext()) { - get; - set; - } + var masterTrunk = + context.MasterTrunk.OrderBy(e => EF.Property(e, "Id")) + .FirstOrDefault(); //exception Sequence contains no elements. - public DbSet EntitiesWithQueryFilterCycle1 { get; set; } - public DbSet EntitiesWithQueryFilterCycle2 { get; set; } - public DbSet EntitiesWithQueryFilterCycle3 { get; set; } + Assert.NotNull(masterTrunk); - public MyContext17253(DbContextOptions options) + AssertSql( +""" +SELECT `t`.`Id`, `t`.`MasterTrunk22340Id`, `t`.`MasterTrunk22340Id0`, `f0`.`CurrencyBag22340MasterTrunk22340Id`, `f0`.`Id`, `f0`.`Amount`, `f0`.`Code`, `s0`.`CurrencyBag22340MasterTrunk22340Id`, `s0`.`Id`, `s0`.`Amount`, `s0`.`Code` +FROM (( + SELECT TOP 1 `m`.`Id`, `f`.`MasterTrunk22340Id`, `s`.`MasterTrunk22340Id` AS `MasterTrunk22340Id0` + FROM (`MasterTrunk` AS `m` + LEFT JOIN `FungibleBag` AS `f` ON `m`.`Id` = `f`.`MasterTrunk22340Id`) + LEFT JOIN `StaticBag` AS `s` ON `m`.`Id` = `s`.`MasterTrunk22340Id` + ORDER BY `m`.`Id` +) AS `t` +LEFT JOIN `FungibleBag_Currencies` AS `f0` ON `t`.`MasterTrunk22340Id` = `f0`.`CurrencyBag22340MasterTrunk22340Id`) +LEFT JOIN `StaticBag_Currencies` AS `s0` ON `t`.`MasterTrunk22340Id0` = `s0`.`CurrencyBag22340MasterTrunk22340Id` +ORDER BY `t`.`Id`, `t`.`MasterTrunk22340Id`, `t`.`MasterTrunk22340Id0`, `f0`.`CurrencyBag22340MasterTrunk22340Id`, `f0`.`Id`, `s0`.`CurrencyBag22340MasterTrunk22340Id` +"""); + } + } + + protected class MyContext22340 : DbContext + { + public MyContext22340(DbContextOptions options) : base(options) { } + public DbSet MasterTrunk { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterSelfReference.Any()); - modelBuilder.Entity() - .HasQueryFilter(e => Set().Any()); - - modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterCycle2.Any()); - modelBuilder.Entity().HasQueryFilter(e => Set().Any()); - modelBuilder.Entity().HasQueryFilter(e => EntitiesWithQueryFilterCycle1.Any()); - } - } + var builder = modelBuilder.Entity(); + builder.Property("Id").ValueGeneratedOnAdd(); + builder.HasKey("Id"); - private JetTestStore CreateDatabase17253() - => CreateTestStore( - () => new MyContext17253(_options), - context => - { - context.EntitiesWithQueryFilterSelfReference.Add( - new EntityWithQueryFilterSelfReference { Name = "EntityWithQueryFilterSelfReference" }); - context.EntitiesReferencingEntityWithQueryFilterSelfReference.Add( - new EntityReferencingEntityWithQueryFilterSelfReference - { - Name = "EntityReferencingEntityWithQueryFilterSelfReference" - }); + builder.OwnsOne( + p => p.FungibleBag, p => + { + p.OwnsMany( + p => p.Currencies, p => + { + p.Property(p => p.Amount).IsConcurrencyToken(); + }); - context.EntitiesWithQueryFilterCycle1.Add(new EntityWithQueryFilterCycle1 { Name = "EntityWithQueryFilterCycle1_1" }); - context.EntitiesWithQueryFilterCycle2.Add(new EntityWithQueryFilterCycle2 { Name = "EntityWithQueryFilterCycle2_1" }); - context.EntitiesWithQueryFilterCycle3.Add(new EntityWithQueryFilterCycle3 { Name = "EntityWithQueryFilterCycle3_1" }); + p.ToTable("FungibleBag"); + }); - context.SaveChanges(); + builder.OwnsOne( + p => p.StaticBag, p => + { + p.OwnsMany( + p => p.Currencies, p => + { + p.Property(p => p.Amount).IsConcurrencyToken(); + }); + p.ToTable("StaticBag"); + }); + } - ClearLog(); - }); + public void Seed() + { + var masterTrunk = new MasterTrunk22340 + { + FungibleBag = new CurrencyBag22340 { Currencies = new[] { new Currency22340 { Amount = 10, Code = 999 } } }, + StaticBag = new CurrencyBag22340 { Currencies = new[] { new Currency22340 { Amount = 555, Code = 111 } } } + }; + Add(masterTrunk); - private class EntityWithQueryFilterSelfReference - { - public int Id { get; set; } - public string Name { get; set; } - } + SaveChanges(); + } - private class EntityReferencingEntityWithQueryFilterSelfReference - { - public int Id { get; set; } - public string Name { get; set; } - } + public class MasterTrunk22340 + { + public CurrencyBag22340 FungibleBag { get; set; } + public CurrencyBag22340 StaticBag { get; set; } + } - private class EntityWithQueryFilterCycle1 - { - public int Id { get; set; } - public string Name { get; set; } - } + public class CurrencyBag22340 + { + public IEnumerable Currencies { get; set; } + } - private class EntityWithQueryFilterCycle2 - { - public int Id { get; set; } - public string Name { get; set; } - } + public class Currency22340 + { + [Column(TypeName = "decimal(18,2)")] + public decimal Amount { get; set; } - private class EntityWithQueryFilterCycle3 - { - public int Id { get; set; } - public string Name { get; set; } + [Column(TypeName = "decimal(18,2)")] + public decimal Code { get; set; } + } } #endregion - #region Bug17276_17099_16759 + #region Issue22568 [ConditionalFact] - public virtual void Expression_tree_constructed_via_interface_works_17276() + public virtual async Task Cycles_in_auto_include() { - using (CreateDatabase17276()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17276(_options)) - { - var query = List17276(context.RemovableEntities); + var principals = context.Set().ToList(); + Assert.Single(principals); + Assert.NotNull(principals[0].Dependent); + Assert.NotNull(principals[0].Dependent.Principal); - AssertSql( - $@"SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `t`.`Id`, `t`.`OwnedEntity_OwnedValue` -FROM `RemovableEntities` AS `r` -LEFT JOIN ( - SELECT `r0`.`Id`, `r0`.`OwnedEntity_OwnedValue`, `r1`.`Id` AS `Id0` - FROM `RemovableEntities` AS `r0` - INNER JOIN `RemovableEntities` AS `r1` ON `r0`.`Id` = `r1`.`Id` - WHERE `r0`.`OwnedEntity_OwnedValue` IS NOT NULL -) AS `t` ON `r`.`Id` = `t`.`Id` -WHERE `r`.`IsRemoved` <> True"); - } + var dependents = context.Set().ToList(); + Assert.Single(dependents); + Assert.NotNull(dependents[0].Principal); + Assert.NotNull(dependents[0].Principal.Dependent); + + AssertSql( +""" +SELECT `p`.`Id`, `d`.`Id`, `d`.`PrincipalId` +FROM `PrincipalOneToOne` AS `p` +LEFT JOIN `DependentOneToOne` AS `d` ON `p`.`Id` = `d`.`PrincipalId` +""", +// +""" +SELECT `d`.`Id`, `d`.`PrincipalId`, `p`.`Id` +FROM `DependentOneToOne` AS `d` +INNER JOIN `PrincipalOneToOne` AS `p` ON `d`.`PrincipalId` = `p`.`Id` +"""); } - } - [ConditionalFact] - public virtual void Expression_tree_constructed_via_interface_for_navigation_works_17099() - { - using (CreateDatabase17276()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17276(_options)) - { - var query = context.Parents - .Where(p => EF.Property(EF.Property(p, "RemovableEntity"), "IsRemoved")) - .ToList(); + ClearLog(); + var principals = context.Set().ToList(); + Assert.Single(principals); + Assert.NotNull(principals[0].Dependents); + Assert.True(principals[0].Dependents.All(e => e.Principal != null)); - AssertSql( - $@"SELECT `p`.`Id`, `p`.`RemovableEntityId` -FROM `Parents` AS `p` -LEFT JOIN `RemovableEntities` AS `r` ON `p`.`RemovableEntityId` = `r`.`Id` -WHERE `r`.`IsRemoved` = True"); - } - } - } + var dependents = context.Set().ToList(); + Assert.Equal(2, dependents.Count); + Assert.True(dependents.All(e => e.Principal != null)); + Assert.True(dependents.All(e => e.Principal.Dependents != null)); + Assert.True(dependents.All(e => e.Principal.Dependents.All(i => i.Principal != null))); - [ConditionalFact] - public virtual void Expression_tree_constructed_via_interface_for_owned_navigation_works_17505() - { - using (CreateDatabase17276()) + AssertSql( +""" +SELECT `p`.`Id`, `d`.`Id`, `d`.`PrincipalId` +FROM `PrincipalOneToMany` AS `p` +LEFT JOIN `DependentOneToMany` AS `d` ON `p`.`Id` = `d`.`PrincipalId` +ORDER BY `p`.`Id` +""", +// +""" +SELECT `d`.`Id`, `d`.`PrincipalId`, `p`.`Id`, `d0`.`Id`, `d0`.`PrincipalId` +FROM (`DependentOneToMany` AS `d` +INNER JOIN `PrincipalOneToMany` AS `p` ON `d`.`PrincipalId` = `p`.`Id`) +LEFT JOIN `DependentOneToMany` AS `d0` ON `p`.`Id` = `d0`.`PrincipalId` +ORDER BY `d`.`Id`, `p`.`Id` +"""); + } + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17276(_options)) - { - var query = context.RemovableEntities - .Where(p => EF.Property(EF.Property(p, "OwnedEntity"), "OwnedValue") == "Abc") - .ToList(); + ClearLog(); + Assert.Equal( + CoreStrings.AutoIncludeNavigationCycle("'PrincipalManyToMany.Dependents', 'DependentManyToMany.Principals'"), + Assert.Throws(() => context.Set().ToList()).Message); - AssertSql( - $@"SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `t`.`Id`, `t`.`OwnedEntity_OwnedValue` -FROM `RemovableEntities` AS `r` -LEFT JOIN ( - SELECT `r0`.`Id`, `r0`.`OwnedEntity_OwnedValue`, `r1`.`Id` AS `Id0` - FROM `RemovableEntities` AS `r0` - INNER JOIN `RemovableEntities` AS `r1` ON `r0`.`Id` = `r1`.`Id` - WHERE `r0`.`OwnedEntity_OwnedValue` IS NOT NULL -) AS `t` ON `r`.`Id` = `t`.`Id` -WHERE `t`.`OwnedEntity_OwnedValue` = 'Abc'"); - } + Assert.Equal( + CoreStrings.AutoIncludeNavigationCycle("'DependentManyToMany.Principals', 'PrincipalManyToMany.Dependents'"), + Assert.Throws(() => context.Set().ToList()).Message); + + context.Set().IgnoreAutoIncludes().ToList(); + context.Set().IgnoreAutoIncludes().ToList(); + + AssertSql( +""" +SELECT `p`.`Id` +FROM `PrincipalManyToMany` AS `p` +""", +// +""" +SELECT `d`.`Id` +FROM `DependentManyToMany` AS `d` +"""); } - } - [ConditionalFact] - public virtual void Expression_tree_constructed_via_interface_works_16759() - { - using (CreateDatabase17276()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17276(_options)) - { - var specification = new Specification17276(1); - var entities = context.Set().Where(specification.Criteria).ToList(); + ClearLog(); + Assert.Equal( + CoreStrings.AutoIncludeNavigationCycle("'CycleA.Bs', 'CycleB.C', 'CycleC.As'"), + Assert.Throws(() => context.Set().ToList()).Message); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__id_0='1'")} + Assert.Equal( + CoreStrings.AutoIncludeNavigationCycle("'CycleB.C', 'CycleC.As', 'CycleA.Bs'"), + Assert.Throws(() => context.Set().ToList()).Message); -SELECT `p`.`Id`, `p`.`RemovableEntityId` -FROM `Parents` AS `p` -WHERE `p`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}"); - } - } - } + Assert.Equal( + CoreStrings.AutoIncludeNavigationCycle("'CycleC.As', 'CycleA.Bs', 'CycleB.C'"), + Assert.Throws(() => context.Set().ToList()).Message); - private class MyContext17276 : DbContext - { - public DbSet RemovableEntities { get; set; } - public DbSet Parents { get; set; } + context.Set().IgnoreAutoIncludes().ToList(); + context.Set().IgnoreAutoIncludes().ToList(); + context.Set().IgnoreAutoIncludes().ToList(); - public MyContext17276(DbContextOptions options) + AssertSql( +""" +SELECT `c`.`Id`, `c`.`CycleCId` +FROM `CycleA` AS `c` +""", +// +""" +SELECT `c`.`Id`, `c`.`CId`, `c`.`CycleAId` +FROM `CycleB` AS `c` +""", +// +""" +SELECT `c`.`Id`, `c`.`BId` +FROM `CycleC` AS `c` +"""); + } + } + + protected class MyContext22568 : DbContext + { + public MyContext22568(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity().Navigation(e => e.Dependent).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Principal).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Dependents).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Principal).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Dependents).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.Principals).AutoInclude(); + + modelBuilder.Entity().Navigation(e => e.Bs).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.C).AutoInclude(); + modelBuilder.Entity().Navigation(e => e.As).AutoInclude(); } - } - private JetTestStore CreateDatabase17276() - => CreateTestStore( - () => new MyContext17276(_options), - context => - { - context.SaveChanges(); + public void Seed() + { + Add(new PrincipalOneToOne { Dependent = new DependentOneToOne() }); + Add( + new PrincipalOneToMany + { + Dependents = new List + { + new(), new(), + } + }); - ClearLog(); - }); + SaveChanges(); + } - private static List List17276(IQueryable query) - where T : IRemovable17276 - { - return query.Where(x => !x.IsRemoved).ToList(); - } + public class PrincipalOneToOne + { + public int Id { get; set; } + public DependentOneToOne Dependent { get; set; } + } - private interface IRemovable17276 - { - bool IsRemoved { get; set; } + public class DependentOneToOne + { + public int Id { get; set; } - string RemovedByUser { get; set; } + [ForeignKey("Principal")] + public int PrincipalId { get; set; } - DateTime? Removed { get; set; } - } + public PrincipalOneToOne Principal { get; set; } + } - private class RemovableEntity17276 : IRemovable17276 - { - public int Id { get; set; } - public bool IsRemoved { get; set; } - public string RemovedByUser { get; set; } - public DateTime? Removed { get; set; } - public OwnedEntity OwnedEntity { get; set; } - } + public class PrincipalOneToMany + { + public int Id { get; set; } + public List Dependents { get; set; } + } - private class Parent17276 : IHasId17276 - { - public int Id { get; set; } - public RemovableEntity17276 RemovableEntity { get; set; } - } + public class DependentOneToMany + { + public int Id { get; set; } - [Owned] - private class OwnedEntity : IOwned - { - public string OwnedValue { get; set; } - } + [ForeignKey("Principal")] + public int PrincipalId { get; set; } - private interface IHasId17276 - { - T Id { get; } - } + public PrincipalOneToMany Principal { get; set; } + } - private interface IOwned - { - public string OwnedValue { get; } - } + public class PrincipalManyToMany + { + public int Id { get; set; } + public List Dependents { get; set; } + } - private class Specification17276 - where T : IHasId17276 - { - public Expression> Criteria { get; } + public class DependentManyToMany + { + public int Id { get; set; } + public List Principals { get; set; } + } + + public class CycleA + { + public int Id { get; set; } + public List Bs { get; set; } + } - public Specification17276(int id) + public class CycleB { - Criteria = t => t.Id == id; + public int Id { get; set; } + public CycleC C { get; set; } + } + + public class CycleC + { + public int Id { get; set; } + + [ForeignKey("B")] + public int BId { get; set; } + + private CycleB B { get; set; } + public List As { get; set; } } } #endregion - #region Bug6864 + #region Issue12274 [ConditionalFact] - public virtual void Implicit_cast_6864() + public virtual async Task Parameterless_ctor_on_inner_DTO_gets_called_for_every_row() { - using (CreateDatabase6864()) - { - using (var context = new MyContext6864(_options)) - { - // Verify no client eval - var query = context.Foos.Where(f => f.String == new Bar6864(1337)).ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - AssertSql( - $@"SELECT `f`.`Id`, `f`.`String` -FROM `Foos` AS `f` -WHERE `f`.`String` = '1337'"); - } + using (var context = contextFactory.CreateContext()) + { + var results = context.Entities.Select( + x => + new MyContext12274.OuterDTO12274 + { + Id = x.Id, + Name = x.Name, + Inner = new MyContext12274.InnerDTO12274() + }).ToList(); + Assert.Equal(4, results.Count); + Assert.False(ReferenceEquals(results[0].Inner, results[1].Inner)); + Assert.False(ReferenceEquals(results[1].Inner, results[2].Inner)); + Assert.False(ReferenceEquals(results[2].Inner, results[3].Inner)); } } - [ConditionalFact] - public virtual void Access_property_of_closure_6864() + protected class MyContext12274 : DbContext { - using (CreateDatabase6864()) + public DbSet Entities { get; set; } + + public MyContext12274(DbContextOptions options) + : base(options) { - using (var context = new MyContext6864(_options)) - { - // Verify no client eval - var bar = new Bar6864(1337); - var query = context.Foos.Where(f => f.String == bar.Value).ToList(); + } - AssertSql( - $@"{AssertSqlHelper.Declaration("@__bar_Value_0='1337' (Size = 255)")} + public void Seed() + { + var e1 = new MyEntity12274 { Name = "1" }; + var e2 = new MyEntity12274 { Name = "2" }; + var e3 = new MyEntity12274 { Name = "3" }; + var e4 = new MyEntity12274 { Name = "4" }; -SELECT `f`.`Id`, `f`.`String` -FROM `Foos` AS `f` -WHERE `f`.`String` = {AssertSqlHelper.Parameter("@__bar_Value_0")}"); - } + Entities.AddRange(e1, e2, e3, e4); + SaveChanges(); } - } - [ConditionalFact] - public virtual void Call_method_on_closure_6864() - { - using (CreateDatabase6864()) + public class MyEntity12274 { - using (var context = new MyContext6864(_options)) - { - // Verify no client eval - var bar = new Bar6864(1337); - var query = context.Foos.Where(f => f.String == bar.ToString()).ToList(); - - AssertSql( - $@"{AssertSqlHelper.Declaration("@__ToString_0='1337' (Size = 255)")} + public int Id { get; set; } + public string Name { get; set; } + } -SELECT `f`.`Id`, `f`.`String` -FROM `Foos` AS `f` -WHERE `f`.`String` = {AssertSqlHelper.Parameter("@__ToString_0")}"); - } + public class OuterDTO12274 + { + public int Id { get; set; } + public string Name { get; set; } + public InnerDTO12274 Inner { get; set; } + } + + public class InnerDTO12274 + { } } + #endregion + + #region Issue11835 + [ConditionalFact] - public virtual void Implicitly_cast_closure_6864() + public virtual async Task Projecting_correlated_collection_along_with_non_mapped_property() { - using (CreateDatabase6864()) - { - using (var context = new MyContext6864(_options)) - { - // Verify no client eval - var bar = new Bar6864(1337); - var query = context.Foos.Where(f => f.String == bar).ToList(); + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__p_0='1337' (Size = 255)")} + using (var context = contextFactory.CreateContext()) + { + var result = context.Blogs.Select( + e => new + { + e.Id, + e.Title, + FirstPostName = e.Posts.Where(i => i.Name.Contains("2")).ToList() + }).ToList(); -SELECT `f`.`Id`, `f`.`String` -FROM `Foos` AS `f` -WHERE `f`.`String` = {AssertSqlHelper.Parameter("@__p_0")}"); - } + AssertSql( +""" +SELECT `b`.`Id`, `t`.`Id`, `t`.`BlogId`, `t`.`Name` +FROM `Blogs` AS `b` +LEFT JOIN ( + SELECT `p`.`Id`, `p`.`BlogId`, `p`.`Name` + FROM `Posts` AS `p` + WHERE `p`.`Name` LIKE '%2%' +) AS `t` ON `b`.`Id` = `t`.`BlogId` +ORDER BY `b`.`Id` +"""); } - } - [ConditionalFact] - public virtual void Implicitly_cast_return_value_6864() - { - using (CreateDatabase6864()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext6864(_options)) - { - // Verify no client eval - var query = context.Foos.Where(f => f.String == new Bar6864(1337).Clone()).ToList(); + ClearLog(); + var result = context.Blogs.Select( + e => new + { + e.Id, + e.Title, + FirstPostName = e.Posts.OrderBy(i => i.Id).FirstOrDefault().Name + }).ToList(); - AssertSql( - $@"SELECT `f`.`Id`, `f`.`String` -FROM `Foos` AS `f` -WHERE `f`.`String` = '1337'"); - } + AssertSql( +""" +SELECT `b`.`Id`, ( + SELECT TOP 1 `p`.`Name` + FROM `Posts` AS `p` + WHERE `b`.`Id` = `p`.`BlogId` + ORDER BY `p`.`Id`) +FROM `Blogs` AS `b` +"""); } } - private class MyContext6864 : DbContext + protected class MyContext11835 : DbContext { - public DbSet Foos { get; set; } + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } - public MyContext6864(DbContextOptions options) + public MyContext11835(DbContextOptions options) : base(options) { } - } - private JetTestStore CreateDatabase6864() - => CreateTestStore( - () => new MyContext6864(_options), - context => - { - context.SaveChanges(); + public void Seed() + { + var b1 = new Blog11835 { Title = "B1" }; + var b2 = new Blog11835 { Title = "B2" }; + var p11 = new Post11835 { Name = "P11", Blog = b1 }; + var p12 = new Post11835 { Name = "P12", Blog = b1 }; + var p13 = new Post11835 { Name = "P13", Blog = b1 }; + var p21 = new Post11835 { Name = "P21", Blog = b2 }; + var p22 = new Post11835 { Name = "P22", Blog = b2 }; - ClearLog(); - }); + Blogs.AddRange(b1, b2); + Posts.AddRange(p11, p12, p13, p21, p22); + SaveChanges(); + } - private class FooEntity6864 - { - public int Id { get; set; } - public string String { get; set; } - } + public class Blog11835 + { + public int Id { get; set; } - private class Bar6864 - { - private readonly int _value; + [NotMapped] + public string Title { get; set; } - public Bar6864(int value) - { - _value = value; + public List Posts { get; set; } } - public string Value => _value.ToString(); - public override string ToString() => Value; - public static implicit operator string(Bar6864 bar) => bar.Value; - public Bar6864 Clone() => new Bar6864(_value); + public class Post11835 + { + public int Id { get; set; } + public int BlogId { get; set; } + public Blog11835 Blog { get; set; } + public string Name { get; set; } + } } #endregion - #region Bug9582 + #region Issue23211 [ConditionalFact] - public virtual void Setting_IsUnicode_generates_unicode_literal_in_SQL() + public virtual async Task Collection_include_on_owner_with_owned_type_mapped_to_different_table() { - using (CreateDatabase9582()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext9582(_options)) - { - // Verify SQL - var query = context.Set().Where(xx => xx.Nombre.Contains("lla")).ToList(); + var owner = context.Set().Include(e => e.Dependents).AsSplitQuery().OrderBy(e => e.Id).Single(); + Assert.NotNull(owner.Dependents); + Assert.Equal(2, owner.Dependents.Count); + Assert.NotNull(owner.Owned1); + Assert.Equal("A", owner.Owned1.Value); + Assert.NotNull(owner.Owned2); + Assert.Equal("B", owner.Owned2.Value); - AssertSql( - $@"SELECT `t`.`Id`, `t`.`Nombre` -FROM `TipoServicio9582` AS `t` -WHERE CHARINDEX('lla', `t`.`Nombre`) > 0"); - } + AssertSql( +""" +SELECT TOP 2 `o`.`Id`, `o0`.`Owner23211Id`, `o0`.`Value`, `o1`.`Owner23211Id`, `o1`.`Value` +FROM (`Owner23211` AS `o` +LEFT JOIN `Owned123211` AS `o0` ON `o`.`Id` = `o0`.`Owner23211Id`) +LEFT JOIN `Owned223211` AS `o1` ON `o`.`Id` = `o1`.`Owner23211Id` +ORDER BY `o`.`Id`, `o0`.`Owner23211Id`, `o1`.`Owner23211Id` +""", +// +""" +SELECT `d`.`Id`, `d`.`Owner23211Id`, `t`.`Id`, `t`.`Owner23211Id`, `t`.`Owner23211Id0` +FROM ( + SELECT TOP 1 `o`.`Id`, `o0`.`Owner23211Id`, `o1`.`Owner23211Id` AS `Owner23211Id0` + FROM (`Owner23211` AS `o` + LEFT JOIN `Owned123211` AS `o0` ON `o`.`Id` = `o0`.`Owner23211Id`) + LEFT JOIN `Owned223211` AS `o1` ON `o`.`Id` = `o1`.`Owner23211Id` + ORDER BY `o`.`Id` +) AS `t` +INNER JOIN `Dependent23211` AS `d` ON `t`.`Id` = `d`.`Owner23211Id` +ORDER BY `t`.`Id`, `t`.`Owner23211Id`, `t`.`Owner23211Id0` +"""); + } + + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + var owner = context.Set().Include(e => e.Dependents).AsSplitQuery().OrderBy(e => e.Id) + .Single(); + Assert.NotNull(owner.Dependents); + Assert.Equal(2, owner.Dependents.Count); + Assert.NotNull(owner.Owned); + Assert.Equal("A", owner.Owned.Value); + + AssertSql( +""" +SELECT TOP 2 `s`.`Id`, `o`.`SecondOwner23211Id`, `o`.`Value` +FROM `SecondOwner23211` AS `s` +LEFT JOIN `Owned23211` AS `o` ON `s`.`Id` = `o`.`SecondOwner23211Id` +ORDER BY `s`.`Id`, `o`.`SecondOwner23211Id` +""", +// +""" +SELECT `s0`.`Id`, `s0`.`SecondOwner23211Id`, `t`.`Id`, `t`.`SecondOwner23211Id` +FROM ( + SELECT TOP 1 `s`.`Id`, `o`.`SecondOwner23211Id` + FROM `SecondOwner23211` AS `s` + LEFT JOIN `Owned23211` AS `o` ON `s`.`Id` = `o`.`SecondOwner23211Id` + ORDER BY `s`.`Id` +) AS `t` +INNER JOIN `SecondDependent23211` AS `s0` ON `t`.`Id` = `s0`.`SecondOwner23211Id` +ORDER BY `t`.`Id`, `t`.`SecondOwner23211Id` +"""); } } - private class MyContext9582 : DbContext + protected class MyContext23211 : DbContext { - public MyContext9582(DbContextOptions options) + public MyContext23211(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity( - builder => + modelBuilder.Entity().OwnsOne(e => e.Owned1, b => b.ToTable("Owned123211")); + modelBuilder.Entity().OwnsOne(e => e.Owned2, b => b.ToTable("Owned223211")); + modelBuilder.Entity().OwnsOne(e => e.Owned, b => b.ToTable("Owned23211")); + } + + public void Seed() + { + Add( + new Owner23211 { - builder.HasKey(ts => ts.Id); + Dependents = new List { new(), new() }, + Owned1 = new OwnedType23211 { Value = "A" }, + Owned2 = new OwnedType23211 { Value = "B" } + }); - builder.Property(ts => ts.Id).IsRequired(); - builder.Property(ts => ts.Nombre).IsRequired().HasMaxLength(20); + Add( + new SecondOwner23211 + { + Dependents = new List { new(), new() }, + Owned = new OwnedType23211 { Value = "A" } }); - foreach (var property in modelBuilder.Model.GetEntityTypes() - .SelectMany(e => e.GetProperties().Where(p => p.ClrType == typeof(string)))) - { - property.SetIsUnicode(false); - } + SaveChanges(); } - } - private JetTestStore CreateDatabase9582() - => CreateTestStore( - () => new MyContext9582(_options), - context => - { - context.SaveChanges(); + public class Owner23211 + { + public int Id { get; set; } + public List Dependents { get; set; } + public OwnedType23211 Owned1 { get; set; } + public OwnedType23211 Owned2 { get; set; } + } - ClearLog(); - }); + public class OwnedType23211 + { + public string Value { get; set; } + } - private class TipoServicio9582 - { - public int Id { get; set; } - public string Nombre { get; set; } + public class Dependent23211 + { + public int Id { get; set; } + } + + public class SecondOwner23211 + { + public int Id { get; set; } + public List Dependents { get; set; } + public OwnedType23211 Owned { get; set; } + } + + public class SecondDependent23211 + { + public int Id { get; set; } + } } #endregion - #region Bug7222 + #region Issue10295 [ConditionalFact] - public virtual void Inlined_dbcontext_is_not_leaking() + public virtual async Task Query_filter_with_contains_evaluates_correctly() { - using (CreateDatabase7222()) + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext7222(_options)) - { - var entities = context.Blogs.Select(b => context.ClientMethod(b)).ToList(); + var result = context.Entities.ToList(); + Assert.Single(result); - AssertSql( - $@"SELECT `b`.`Id` -FROM `Blogs` AS `b`"); - } + AssertSql( +""" +SELECT `e`.`Id`, `e`.`Name` +FROM `Entities` AS `e` +WHERE `e`.`Id` NOT IN (1, 7) +"""); } } - [ConditionalFact] - public virtual void Implicit_DbContext_throws_memory_leak() + protected class MyContext10295 : DbContext { - using (CreateDatabase7222()) - { - using (var context = new MyContext7222(_options)) - { - Assert.Throws(() => context.RunQuery()); - } - } - } + private readonly List _ids = new() { 1, 7 }; - private class MyContext7222 : DbContext - { - public DbSet Blogs { get; set; } + public DbSet Entities { get; set; } - public MyContext7222() + public MyContext10295(DbContextOptions options) + : base(options) { } - public MyContext7222(DbContextOptions options) - : base(options) + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasQueryFilter(x => !_ids.Contains(x.Id)); + + public void Seed() { + var e1 = new MyEntity10295 { Name = "Name1" }; + var e2 = new MyEntity10295 { Name = "Name2" }; + Entities.AddRange(e1, e2); + SaveChanges(); } - public void RunQuery() + public class MyEntity10295 { - Blogs.Select(b => ClientMethod(b)).ToList(); + public int Id { get; set; } + public string Name { get; set; } } - - public int ClientMethod(Blog7222 blog) => blog.Id; } - private JetTestStore CreateDatabase7222() - => CreateTestStore( - () => new MyContext7222(_options), - context => - { - context.SaveChanges(); + #endregion - ClearLog(); - }); + #region Issue19253 - private class Blog7222 - { - public int Id { get; set; } - } + [ConditionalFact] + public virtual async Task Operators_combine_nullability_of_entity_shapers() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using (var context = contextFactory.CreateContext()) + { + Expression> leftKeySelector = x => x.forkey; + Expression> rightKeySelector = y => y.forkey; + + var query = context.A.GroupJoin( + context.B, + leftKeySelector, + rightKeySelector, + (left, rightg) => new { left, rightg }) + .SelectMany( + r => r.rightg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 { Left = x.left, Right = y }) + .Concat( + context.B.GroupJoin( + context.A, + rightKeySelector, + leftKeySelector, + (right, leftg) => new { leftg, right }) + .SelectMany( + l => l.leftg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 + { + Left = y, + Right = x.right + }) + .Where(z => z.Left.Equals(null))) + .ToList(); - #endregion + Assert.Equal(3, query.Count); - #region Bug17644 + AssertSql( +""" +SELECT `a`.`Id`, `a`.`a`, `a`.`a1`, `a`.`forkey`, `b`.`Id` AS `Id0`, `b`.`b`, `b`.`b1`, `b`.`forkey` AS `forkey0` +FROM `A` AS `a` +LEFT JOIN `B` AS `b` ON `a`.`forkey` = `b`.`forkey` +UNION ALL +SELECT `a0`.`Id`, `a0`.`a`, `a0`.`a1`, `a0`.`forkey`, `b0`.`Id` AS `Id0`, `b0`.`b`, `b0`.`b1`, `b0`.`forkey` AS `forkey0` +FROM `B` AS `b0` +LEFT JOIN `A` AS `a0` ON `b0`.`forkey` = `a0`.`forkey` +WHERE `a0`.`Id` IS NULL +"""); + } - [ConditionalFact] - public virtual async Task Return_type_of_First_is_preserved() - { - using (CreateDatabase17644()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17644(_options)) - { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .FirstAsync(); + ClearLog(); + Expression> leftKeySelector = x => x.forkey; + Expression> rightKeySelector = y => y.forkey; + + var query = context.A.GroupJoin( + context.B, + leftKeySelector, + rightKeySelector, + (left, rightg) => new { left, rightg }) + .SelectMany( + r => r.rightg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 { Left = x.left, Right = y }) + .Union( + context.B.GroupJoin( + context.A, + rightKeySelector, + leftKeySelector, + (right, leftg) => new { leftg, right }) + .SelectMany( + l => l.leftg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 + { + Left = y, + Right = x.right + }) + .Where(z => z.Left.Equals(null))) + .ToList(); - AssertSql( - $@"SELECT TOP 1 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21"); - } + Assert.Equal(3, query.Count); + + AssertSql( +""" +SELECT `a`.`Id`, `a`.`a`, `a`.`a1`, `a`.`forkey`, `b`.`Id` AS `Id0`, `b`.`b`, `b`.`b1`, `b`.`forkey` AS `forkey0` +FROM `A` AS `a` +LEFT JOIN `B` AS `b` ON `a`.`forkey` = `b`.`forkey` +UNION +SELECT `a0`.`Id`, `a0`.`a`, `a0`.`a1`, `a0`.`forkey`, `b0`.`Id` AS `Id0`, `b0`.`b`, `b0`.`b1`, `b0`.`forkey` AS `forkey0` +FROM `B` AS `b0` +LEFT JOIN `A` AS `a0` ON `b0`.`forkey` = `a0`.`forkey` +WHERE `a0`.`Id` IS NULL +"""); } - } - [ConditionalFact] - public virtual async Task Return_type_of_FirstOrDefault_is_preserved() - { - using (CreateDatabase17644()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17644(_options)) - { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .FirstOrDefaultAsync(); + ClearLog(); + Expression> leftKeySelector = x => x.forkey; + Expression> rightKeySelector = y => y.forkey; + + var query = context.A.GroupJoin( + context.B, + leftKeySelector, + rightKeySelector, + (left, rightg) => new { left, rightg }) + .SelectMany( + r => r.rightg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 { Left = x.left, Right = y }) + .Except( + context.B.GroupJoin( + context.A, + rightKeySelector, + leftKeySelector, + (right, leftg) => new { leftg, right }) + .SelectMany( + l => l.leftg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 + { + Left = y, + Right = x.right + })) + .ToList(); - AssertSql( - $@"SELECT TOP 1 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21"); - } + Assert.Single(query); + + AssertSql( + """ +SELECT [a].[Id], [a].[a], [a].[a1], [a].[forkey], [b].[Id] AS [Id0], [b].[b], [b].[b1], [b].[forkey] AS [forkey0] +FROM [A] AS [a] +LEFT JOIN [B] AS [b] ON [a].[forkey] = [b].[forkey] +EXCEPT +SELECT [a0].[Id], [a0].[a], [a0].[a1], [a0].[forkey], [b0].[Id] AS [Id0], [b0].[b], [b0].[b1], [b0].[forkey] AS [forkey0] +FROM [B] AS [b0] +LEFT JOIN [A] AS [a0] ON [b0].[forkey] = [a0].[forkey] +"""); } - } - [ConditionalFact] - public virtual async Task Return_type_of_Single_is_preserved() - { - using (CreateDatabase17644()) + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext17644(_options)) - { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .SingleAsync(); + ClearLog(); + Expression> leftKeySelector = x => x.forkey; + Expression> rightKeySelector = y => y.forkey; + + var query = context.A.GroupJoin( + context.B, + leftKeySelector, + rightKeySelector, + (left, rightg) => new { left, rightg }) + .SelectMany( + r => r.rightg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 { Left = x.left, Right = y }) + .Intersect( + context.B.GroupJoin( + context.A, + rightKeySelector, + leftKeySelector, + (right, leftg) => new { leftg, right }) + .SelectMany( + l => l.leftg.DefaultIfEmpty(), + (x, y) => new MyContext19253.JoinResult19253 + { + Left = y, + Right = x.right + })) + .ToList(); - AssertSql( - $@"SELECT TOP 2 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21"); - } + Assert.Single(query); + + AssertSql( + """ +SELECT [a].[Id], [a].[a], [a].[a1], [a].[forkey], [b].[Id] AS [Id0], [b].[b], [b].[b1], [b].[forkey] AS [forkey0] +FROM [A] AS [a] +LEFT JOIN [B] AS [b] ON [a].[forkey] = [b].[forkey] +INTERSECT +SELECT [a0].[Id], [a0].[a], [a0].[a1], [a0].[forkey], [b0].[Id] AS [Id0], [b0].[b], [b0].[b1], [b0].[forkey] AS [forkey0] +FROM [B] AS [b0] +LEFT JOIN [A] AS [a0] ON [b0].[forkey] = [a0].[forkey] +"""); } } - [ConditionalFact] - public virtual async Task Return_type_of_SingleOrDefault_is_preserved() + public class MyContext19253 : DbContext { - using (CreateDatabase17644()) - { - using (var context = new MyContext17644(_options)) - { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .SingleOrDefaultAsync(); + public DbSet A { get; set; } + public DbSet B { get; set; } - AssertSql( - $@"SELECT TOP 2 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21"); - } + public MyContext19253(DbContextOptions options) + : base(options) + { } - } - [ConditionalFact] - public virtual async Task Return_type_of_Last_is_preserved() - { - using (CreateDatabase17644()) + public void Seed() { - using (var context = new MyContext17644(_options)) + var tmp_a = new[] + { + new() + { + a = "a0", + a1 = "a1", + forkey = "a" + }, + new A19253 { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .OrderBy(p => p.Id) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .LastAsync(); + a = "a2", + a1 = "a1", + forkey = "d" + }, + }; + var tmp_b = new[] + { + new() + { + b = "b0", + b1 = "b1", + forkey = "a" + }, + new B19253 + { + b = "b2", + b1 = "b1", + forkey = "c" + }, + }; + A.AddRange(tmp_a); + B.AddRange(tmp_b); + SaveChanges(); + } - AssertSql( - $@"SELECT TOP 1 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21 -ORDER BY `p`.`Id` DESC"); - } + public class JoinResult19253 + { + public TLeft Left { get; set; } + + public TRight Right { get; set; } } - } - [ConditionalFact] - public virtual async Task Return_type_of_LastOrDefault_is_preserved() - { - using (CreateDatabase17644()) + public class A19253 { - using (var context = new MyContext17644(_options)) - { - var personsToFind = await context.Persons.Where(p => p.Age >= 21) - .OrderBy(p => p.Id) - .Select(p => new PersonDetailView17644 { Name = p.Name, Age = p.Age }) - .LastOrDefaultAsync(); + public int Id { get; set; } + public string a { get; set; } + public string a1 { get; set; } + public string forkey { get; set; } + } - AssertSql( - $@"SELECT TOP 1 `p`.`Name`, `p`.`Age` -FROM `Persons` AS `p` -WHERE `p`.`Age` >= 21 -ORDER BY `p`.`Id` DESC"); - } + public class B19253 + { + public int Id { get; set; } + public string b { get; set; } + public string b1 { get; set; } + public string forkey { get; set; } } } - private class MyContext17644 : DbContext + #endregion + + #region Issue22841 + + [ConditionalFact] + public async Task SaveChangesAsync_accepts_changes_with_ConfigureAwait_true_22841() { - public DbSet Persons { get; set; } + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + var observableThing = new ObservableThing22841(); + + using var trackingSynchronizationContext = new SingleThreadSynchronizationContext(); + var origSynchronizationContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext(trackingSynchronizationContext); + + // Do a dispatch once to make sure we're in the new synchronization context. This is necessary in case the below happens + // to complete synchronously, which shouldn't happen in principle - but just to be safe. + await Task.Delay(1).ConfigureAwait(true); - public MyContext17644() + bool? isMySyncContext = null; + Action callback = () => isMySyncContext = + SynchronizationContext.Current == trackingSynchronizationContext + && Thread.CurrentThread == trackingSynchronizationContext.Thread; + observableThing.Event += callback; + + try + { + await context.AddAsync(observableThing); + await context.SaveChangesAsync(); + } + finally { + observableThing.Event -= callback; + SynchronizationContext.SetSynchronizationContext(origSynchronizationContext); } - public MyContext17644(DbContextOptions options) + Assert.True(isMySyncContext); + } + + protected class MyContext22841 : DbContext + { + public MyContext22841(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder + .Entity() + .Property(o => o.Id) + .UsePropertyAccessMode(PropertyAccessMode.Property); + + public DbSet ObservableThings { get; set; } } - private JetTestStore CreateDatabase17644() - => CreateTestStore( - () => new MyContext17644(_options), - context => + public class ObservableThing22841 + { + public int Id + { + get => _id; + set { - var person = new Person17644 { Name = "John Doe", Age = 21 }; - context.Persons.Add(person); - context.SaveChanges(); + _id = value; + Event?.Invoke(); + } + } - ClearLog(); - }); + private int _id; - private class Person17644 - { - public int Id { get; set; } - public string Name { set; get; } - public int Age { set; get; } + public event Action Event; } - private class PersonView17644 + #endregion Issue22841 + + #region Issue12482 + + [ConditionalFact] + public virtual async Task Batch_insert_with_sqlvariant_different_types_12482() { - public string Name { set; get; } + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + context.AddRange( + new MyContext12482.BaseEntity12482 { Value = 10.0999 }, + new MyContext12482.BaseEntity12482 { Value = -12345 }, + new MyContext12482.BaseEntity12482 { Value = "String Value" }, + new MyContext12482.BaseEntity12482 { Value = new DateTime(2020, 1, 1) }); + + context.SaveChanges(); + + AssertSql( + """ +@p0='10.0999' (Nullable = true) (DbType = Object) +@p1='-12345' (Nullable = true) (DbType = Object) +@p2='String Value' (Size = 12) (DbType = Object) +@p3='2020-01-01T00:00:00.0000000' (Nullable = true) (DbType = Object) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +MERGE [BaseEntities] USING ( +VALUES (@p0, 0), +(@p1, 1), +(@p2, 2), +(@p3, 3)) AS i ([Value], _Position) ON 1=0 +WHEN NOT MATCHED THEN +INSERT ([Value]) +VALUES (i.[Value]) +OUTPUT INSERTED.[Id], i._Position; +"""); + } } - private class PersonDetailView17644 : PersonView17644 + protected class MyContext12482 : DbContext { - public int Age { set; get; } + public virtual DbSet BaseEntities { get; set; } + + public MyContext12482(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity(); + + public class BaseEntity12482 + { + public int Id { get; set; } + + [Column(TypeName = "sql_variant")] + public object Value { get; set; } + } } #endregion - #region Bug11023 + #region Issue23674 [ConditionalFact] - public virtual async Task Async_correlated_projection_with_first() + public virtual async Task Walking_back_include_tree_is_not_allowed_1() { - using (CreateDatabase11023()) + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) { - using (var context = new MyContext11023(_options)) - { - var query = await context.Entities - .Select(e => new { ThingIds = e.Values.First().Things.Select(t => t.Subthing.ThingId).ToList() }) - .ToListAsync(); + var query = context.Set() + .Include(p => p.ManyDependents) + .ThenInclude(m => m.Principal.SingleDependent); - var result = Assert.Single(query); - Assert.Equal(new[] { 1, 2 }, result.ThingIds); + Assert.Equal( + CoreStrings.WarningAsErrorTemplate( + CoreEventId.NavigationBaseIncludeIgnored.ToString(), + CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger()) + .GenerateMessage("ManyDependent23674.Principal"), + "CoreEventId.NavigationBaseIncludeIgnored"), + Assert.Throws( + () => query.ToList()).Message); + } + } - AssertSql( - $@"SELECT `e`.`Id`, `t0`.`ThingId`, `t0`.`Id` -FROM `Entities` AS `e` -OUTER APPLY ( - SELECT `s`.`ThingId`, `t`.`Id` - FROM `Things` AS `t` - LEFT JOIN `Subthings` AS `s` ON `t`.`Id` = `s`.`ThingId` - WHERE ( - SELECT TOP 1 `v`.`Id` - FROM `Values` AS `v` - WHERE `e`.`Id` = `v`.`Entity11023Id`) IS NOT NULL AND ((( - SELECT TOP 1 `v0`.`Id` - FROM `Values` AS `v0` - WHERE `e`.`Id` = `v0`.`Entity11023Id`) = `t`.`Value11023Id`) OR (( - SELECT TOP 1 `v0`.`Id` - FROM `Values` AS `v0` - WHERE `e`.`Id` = `v0`.`Entity11023Id`) IS NULL AND `t`.`Value11023Id` IS NULL)) -) AS `t0` -ORDER BY `e`.`Id`, `t0`.`Id`"); - } + [ConditionalFact] + public virtual async Task Walking_back_include_tree_is_not_allowed_2() + { + var contextFactory = await InitializeAsync(); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Include(p => p.SingleDependent.Principal.ManyDependents); + + Assert.Equal( + CoreStrings.WarningAsErrorTemplate( + CoreEventId.NavigationBaseIncludeIgnored.ToString(), + CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger()) + .GenerateMessage("SingleDependent23674.Principal"), + "CoreEventId.NavigationBaseIncludeIgnored"), + Assert.Throws( + () => query.ToList()).Message); } } - private class MyContext11023 : DbContext + [ConditionalFact] + public virtual async Task Walking_back_include_tree_is_not_allowed_3() { - public DbSet Entities { get; set; } - public DbSet Values { get; set; } - public DbSet Things { get; set; } - public DbSet Subthings { get; set; } + var contextFactory = await InitializeAsync(); - public MyContext11023(DbContextOptions options) - : base(options) + using (var context = contextFactory.CreateContext()) { + // This does not warn because after round-tripping from one-to-many from dependent side, the number of dependents could be larger. + var query = context.Set() + .Include(p => p.Principal.ManyDependents) + .ThenInclude(m => m.SingleDependent) + .ToList(); } } - private JetTestStore CreateDatabase11023() - => CreateTestStore( - () => new MyContext11023(_options), - context => - { - context.Add( - new Entity11023 - { - Values = new List - { - new Value11023 - { - Things = new List - { - new Thing11023 { Subthing = new Subthing11023() }, - new Thing11023 { Subthing = new Subthing11023() } - } - } - } - }); + [ConditionalFact] + public virtual async Task Walking_back_include_tree_is_not_allowed_4() + { + var contextFactory = await InitializeAsync(); - context.SaveChanges(); + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Include(p => p.ManyDependent.SingleDependent.Principal); - ClearLog(); - }); + Assert.Equal( + CoreStrings.WarningAsErrorTemplate( + CoreEventId.NavigationBaseIncludeIgnored.ToString(), + CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger()) + .GenerateMessage("ManyDependent23674.SingleDependent"), + "CoreEventId.NavigationBaseIncludeIgnored"), + Assert.Throws( + () => query.ToList()).Message); + } + } - private class Entity11023 + private class Principal23674 { public int Id { get; set; } - public ICollection Values { get; set; } + public List ManyDependents { get; set; } + public SingleDependent23674 SingleDependent { get; set; } } - private class Value11023 + private class ManyDependent23674 { public int Id { get; set; } - public ICollection Things { get; set; } + public Principal23674 Principal { get; set; } + public SingleDependent23674 SingleDependent { get; set; } } - private class Thing11023 + private class SingleDependent23674 { public int Id { get; set; } - public Subthing11023 Subthing { get; set; } + public Principal23674 Principal { get; set; } + public int PrincipalId { get; set; } + public int ManyDependentId { get; set; } + public ManyDependent23674 ManyDependent { get; set; } } - private class Subthing11023 + private class MyContext23674 : DbContext { - public int Id { get; set; } - public int ThingId { get; set; } - public Thing11023 Thing { get; set; } + public MyContext23674(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity(); } #endregion - #region Issue7973 + #region Issue23676 [ConditionalFact] - public virtual void SelectMany_with_collection_selector_having_subquery() + public virtual async Task Projection_with_multiple_includes_and_subquery_with_set_operation() { - using (CreateDatabase7973()) - { - using (var context = new MyContext7973(_options)) - { - var users = (from user in context.Users - from organisation in context.Organisations.Where(o => o.OrganisationUsers.Any()).DefaultIfEmpty() - select new { UserId = user.Id, OrgId = organisation.Id }).ToList(); + var contextFactory = await InitializeAsync(); - Assert.Equal(2, users.Count); - - AssertSql( - $@"SELECT `u`.`Id` AS `UserId`, `t0`.`Id` AS `OrgId` -FROM `Users` AS `u`, -( - SELECT `t`.`Id` - FROM ( - SELECT NULL AS `empty` - ) AS `empty` - LEFT JOIN ( - SELECT `o`.`Id` - FROM `Organisations` AS `o` - WHERE EXISTS ( - SELECT 1 - FROM `OrganisationUser7973` AS `o0` - WHERE `o`.`Id` = `o0`.`OrganisationId`) - ) AS `t` ON 1 = 1 -) AS `t0`"); - } - } - } + using var context = contextFactory.CreateContext(); + var id = 1; + var person = await context.Persons + .Include(p => p.Images) + .Include(p => p.Actor) + .ThenInclude(a => a.Movies) + .ThenInclude(p => p.Movie) + .Include(p => p.Director) + .ThenInclude(a => a.Movies) + .ThenInclude(p => p.Movie) + .Select( + x => new + { + x.Id, + x.Name, + x.Surname, + x.Birthday, + x.Hometown, + x.Bio, + x.AvatarUrl, + Images = x.Images + .Select( + i => new + { + i.Id, + i.ImageUrl, + i.Height, + i.Width + }).ToList(), + KnownByFilms = x.Actor.Movies + .Select(m => m.Movie) + .Union( + x.Director.Movies + .Select(m => m.Movie)) + .Select( + m => new + { + m.Id, + m.Name, + m.PosterUrl, + m.Rating + }).ToList() + }) + .FirstOrDefaultAsync(x => x.Id == id); + + // Verify the valid generated SQL + AssertSql( + """ +@__id_0='1' - private class MyContext7973 : DbContext +SELECT [t].[Id], [t].[Name], [t].[Surname], [t].[Birthday], [t].[Hometown], [t].[Bio], [t].[AvatarUrl], [t].[Id0], [t].[Id1], [p0].[Id], [p0].[ImageUrl], [p0].[Height], [p0].[Width], [t0].[Id], [t0].[Name], [t0].[PosterUrl], [t0].[Rating] +FROM ( + SELECT TOP(1) [p].[Id], [p].[Name], [p].[Surname], [p].[Birthday], [p].[Hometown], [p].[Bio], [p].[AvatarUrl], [a].[Id] AS [Id0], [d].[Id] AS [Id1] + FROM [Persons] AS [p] + LEFT JOIN [ActorEntity] AS [a] ON [p].[Id] = [a].[PersonId] + LEFT JOIN [DirectorEntity] AS [d] ON [p].[Id] = [d].[PersonId] + WHERE [p].[Id] = @__id_0 +) AS [t] +LEFT JOIN [PersonImageEntity] AS [p0] ON [t].[Id] = [p0].[PersonId] +OUTER APPLY ( + SELECT [m0].[Id], [m0].[Budget], [m0].[Description], [m0].[DurationInMins], [m0].[Name], [m0].[PosterUrl], [m0].[Rating], [m0].[ReleaseDate], [m0].[Revenue] + FROM [MovieActorEntity] AS [m] + INNER JOIN [MovieEntity] AS [m0] ON [m].[MovieId] = [m0].[Id] + WHERE [t].[Id0] IS NOT NULL AND [t].[Id0] = [m].[ActorId] + UNION + SELECT [m2].[Id], [m2].[Budget], [m2].[Description], [m2].[DurationInMins], [m2].[Name], [m2].[PosterUrl], [m2].[Rating], [m2].[ReleaseDate], [m2].[Revenue] + FROM [MovieDirectorEntity] AS [m1] + INNER JOIN [MovieEntity] AS [m2] ON [m1].[MovieId] = [m2].[Id] + WHERE [t].[Id1] IS NOT NULL AND [t].[Id1] = [m1].[DirectorId] +) AS [t0] +ORDER BY [t].[Id], [t].[Id0], [t].[Id1], [p0].[Id] +"""); + } + + private class PersonEntity { - public DbSet Users { get; set; } - public DbSet Organisations { get; set; } - - public MyContext7973(DbContextOptions options) - : base(options) - { - } + public int Id { get; set; } + public string Name { get; set; } + public string Surname { get; set; } + public DateTime Birthday { get; set; } + public string Hometown { get; set; } + public string Bio { get; set; } + public string AvatarUrl { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().HasKey(ou => new { ou.OrganisationId, ou.UserId }); - modelBuilder.Entity().HasOne(ou => ou.Organisation).WithMany(o => o.OrganisationUsers) - .HasForeignKey(ou => ou.OrganisationId); - modelBuilder.Entity().HasOne(ou => ou.User).WithMany(u => u.OrganisationUsers) - .HasForeignKey(ou => ou.UserId); - } + public ActorEntity Actor { get; set; } + public DirectorEntity Director { get; set; } + public IList Images { get; } = new List(); } - private JetTestStore CreateDatabase7973() - => CreateTestStore( - () => new MyContext7973(_options), - context => - { - context.AddRange( - new OrganisationUser7973 { Organisation = new Organisation7973(), User = new User7973() }, - new Organisation7973(), - new User7973()); - - context.SaveChanges(); - ClearLog(); - }); - - private class User7973 + private class PersonImageEntity { public int Id { get; set; } - public List OrganisationUsers { get; set; } + public string ImageUrl { get; set; } + public int Height { get; set; } + public int Width { get; set; } + public PersonEntity Person { get; set; } } - private class Organisation7973 + private class ActorEntity { public int Id { get; set; } - public List OrganisationUsers { get; set; } + public int PersonId { get; set; } + public PersonEntity Person { get; set; } + + public IList Movies { get; } = new List(); } - private class OrganisationUser7973 + private class MovieActorEntity { - public int OrganisationId { get; set; } - public Organisation7973 Organisation { get; set; } + public int Id { get; set; } + public int ActorId { get; set; } + public ActorEntity Actor { get; set; } - public int UserId { get; set; } - public User7973 User { get; set; } - } + public int MovieId { get; set; } + public MovieEntity Movie { get; set; } - #endregion + public string RoleInFilm { get; set; } - #region Issue10447 + public int Order { get; set; } + } - [ConditionalFact] - public virtual void Nested_include_queries_do_not_populate_navigation_twice() + private class DirectorEntity { - using (CreateDatabase10447()) - { - using (var context = new MyContext10447(_options)) - { - var query = context.Blogs.Include(b => b.Posts); - - foreach (var blog in query) - { - query.ToList(); - } + public int Id { get; set; } + public int PersonId { get; set; } + public PersonEntity Person { get; set; } - Assert.Collection( - query, - b => Assert.Equal(3, b.Posts.Count), - b => Assert.Equal(2, b.Posts.Count), - b => Assert.Single(b.Posts)); - } - } + public IList Movies { get; } = new List(); } - private class MyContext10447 : DbContext + private class MovieDirectorEntity { - public DbSet Blogs { get; set; } - - public MyContext10447(DbContextOptions options) - : base(options) - { - } + public int Id { get; set; } + public int DirectorId { get; set; } + public DirectorEntity Director { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - } + public int MovieId { get; set; } + public MovieEntity Movie { get; set; } } - private JetTestStore CreateDatabase10447() - => CreateTestStore( - () => new MyContext10447(_options), - context => - { - context.AddRange( - new Blog10447 - { - Posts = new List - { - new Post10447(), - new Post10447(), - new Post10447() - } - }, - new Blog10447 { Posts = new List { new Post10447(), new Post10447() } }, - new Blog10447 { Posts = new List { new Post10447() } }); - - context.SaveChanges(); - ClearLog(); - }); - - private class Blog10447 + private class MovieEntity { public int Id { get; set; } - public List Posts { get; set; } + public string Name { get; set; } + public double Rating { get; set; } + public string Description { get; set; } + public DateTime ReleaseDate { get; set; } + public int DurationInMins { get; set; } + public int Budget { get; set; } + public int Revenue { get; set; } + public string PosterUrl { get; set; } + + public IList Directors { get; set; } = new List(); + public IList Actors { get; set; } = new List(); } - private class Post10447 + private class MyContext23676 : DbContext { - public int Id { get; set; } + public MyContext23676(DbContextOptions options) + : base(options) + { + } + + public DbSet Persons { get; set; } - public Blog10447 Blog { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } } #endregion - #region Issue12456 + #region Issue19947 [ConditionalFact] - public virtual void Let_multiple_references_with_reference_to_outer() + public virtual async Task Multiple_select_many_in_projection() { - using (CreateDatabase12456()) - { - using (var context = new MyContext12456(_options)) - { - var users = (from a in context.Activities - let cs = context.CompetitionSeasons - .First(s => s.StartDate <= a.DateTime && a.DateTime < s.EndDate) - select new { cs.Id, Points = a.ActivityType.Points.Where(p => p.CompetitionSeason == cs) }).ToList(); + var contextFactory = await InitializeAsync(); - AssertSql( - $@"SELECT ( - SELECT TOP 1 `c`.`Id` - FROM `CompetitionSeasons` AS `c` - WHERE (`c`.`StartDate` <= `a`.`DateTime`) AND (`a`.`DateTime` < `c`.`EndDate`)), `a`.`Id`, `a0`.`Id`, `t`.`Id`, `t`.`ActivityTypeId`, `t`.`CompetitionSeasonId`, `t`.`Points`, `t`.`Id0` -FROM `Activities` AS `a` -INNER JOIN `ActivityType12456` AS `a0` ON `a`.`ActivityTypeId` = `a0`.`Id` + using var context = contextFactory.CreateContext(); + var query = context.Users.Select( + captain => new + { + CaptainRateDtos = captain.Cars + .SelectMany(car0 => car0.Taxis) + .OrderByDescending(taxi => taxi.DateArrived).Take(12) + .Select( + taxi => new + { + Rate = taxi.UserRate.Value, + UserRateText = taxi.UserTextRate, + UserId = taxi.UserEUser.Id, + }).ToList(), + ReportCount = captain.Cars + .SelectMany(car1 => car1.Taxis).Count(taxi0 => taxi0.ReportText != ""), + }).SingleOrDefault(); + + // Verify the valid generated SQL + AssertSql( + """ +SELECT [t].[Id], [t1].[Rate], [t1].[UserRateText], [t1].[UserId], [t1].[Id], [t1].[Id0], [t].[c] +FROM ( + SELECT TOP(2) ( + SELECT COUNT(*) + FROM [Cars] AS [c] + INNER JOIN [Taxis] AS [t0] ON [c].[Id] = [t0].[CarId] + WHERE [u].[Id] = [c].[EUserId] AND ([t0].[ReportText] <> N'' OR [t0].[ReportText] IS NULL)) AS [c], [u].[Id] + FROM [Users] AS [u] +) AS [t] OUTER APPLY ( - SELECT `a1`.`Id`, `a1`.`ActivityTypeId`, `a1`.`CompetitionSeasonId`, `a1`.`Points`, `c0`.`Id` AS `Id0` - FROM `ActivityTypePoints12456` AS `a1` - INNER JOIN `CompetitionSeasons` AS `c0` ON `a1`.`CompetitionSeasonId` = `c0`.`Id` - WHERE (`c0`.`Id` = ( - SELECT TOP 1 `c1`.`Id` - FROM `CompetitionSeasons` AS `c1` - WHERE (`c1`.`StartDate` <= `a`.`DateTime`) AND (`a`.`DateTime` < `c1`.`EndDate`))) AND (`a0`.`Id` = `a1`.`ActivityTypeId`) -) AS `t` -ORDER BY `a`.`Id`, `a0`.`Id`, `t`.`Id`, `t`.`Id0`"); - } - } + SELECT [t2].[UserRate] AS [Rate], [t2].[UserTextRate] AS [UserRateText], [u0].[Id] AS [UserId], [t2].[Id], [t2].[Id0], [t2].[DateArrived] + FROM ( + SELECT TOP(12) [c0].[Id], [t3].[Id] AS [Id0], [t3].[DateArrived], [t3].[UserEUserId], [t3].[UserRate], [t3].[UserTextRate] + FROM [Cars] AS [c0] + INNER JOIN [Taxis] AS [t3] ON [c0].[Id] = [t3].[CarId] + WHERE [t].[Id] = [c0].[EUserId] + ORDER BY [t3].[DateArrived] DESC + ) AS [t2] + LEFT JOIN [Users] AS [u0] ON [t2].[UserEUserId] = [u0].[Id] +) AS [t1] +ORDER BY [t].[Id], [t1].[DateArrived] DESC, [t1].[Id], [t1].[Id0] +"""); } [ConditionalFact] - public virtual void Let_multiple_references_with_reference_to_outer_2() + public virtual async Task Single_select_many_in_projection_with_take() { - using (CreateDatabase12456()) - { - using (var context = new MyContext12456(_options)) + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + var query = context.Users.Select( + captain => new { - var users = context.Activities + CaptainRateDtos = captain.Cars + .SelectMany(car0 => car0.Taxis) + .OrderByDescending(taxi => taxi.DateArrived).Take(12) .Select( - a => new + taxi => new { - Activity = a, - CompetitionSeason = context.CompetitionSeasons - .First(s => s.StartDate <= a.DateTime && a.DateTime < s.EndDate) - }) - .Select( - a => new - { - a.Activity, - CompetitionSeasonId = a.CompetitionSeason.Id, - Points = a.Activity.Points - ?? a.Activity.ActivityType.Points - .Where(p => p.CompetitionSeason == a.CompetitionSeason) - .Select(p => p.Points).SingleOrDefault() - }).ToList(); - - AssertSql( - $@"SELECT `a0`.`Id`, `a0`.`ActivityTypeId`, `a0`.`DateTime`, `a0`.`Points`, ( - SELECT TOP 1 `c`.`Id` - FROM `CompetitionSeasons` AS `c` - WHERE (`c`.`StartDate` <= `a0`.`DateTime`) AND (`a0`.`DateTime` < `c`.`EndDate`)) AS `CompetitionSeasonId`, COALESCE(`a0`.`Points`, ( - SELECT TOP 1 `a`.`Points` - FROM `ActivityTypePoints12456` AS `a` - INNER JOIN `CompetitionSeasons` AS `c0` ON `a`.`CompetitionSeasonId` = `c0`.`Id` - WHERE (`a1`.`Id` = `a`.`ActivityTypeId`) AND (`c0`.`Id` = ( - SELECT TOP 1 `c1`.`Id` - FROM `CompetitionSeasons` AS `c1` - WHERE (`c1`.`StartDate` <= `a0`.`DateTime`) AND (`a0`.`DateTime` < `c1`.`EndDate`))))) AS `Points` -FROM `Activities` AS `a0` -INNER JOIN `ActivityType12456` AS `a1` ON `a0`.`ActivityTypeId` = `a1`.`Id`"); - } - } - } - - private class MyContext12456 : DbContext - { - public DbSet Activities { get; set; } - public DbSet CompetitionSeasons { get; set; } + Rate = taxi.UserRate.Value, + UserRateText = taxi.UserTextRate, + UserId = taxi.UserEUser.Id, + }).ToList() + }).SingleOrDefault(); - public MyContext12456(DbContextOptions options) - : base(options) - { - } + // Verify the valid generated SQL + AssertSql( + """ +SELECT [t].[Id], [t1].[Rate], [t1].[UserRateText], [t1].[UserId], [t1].[Id], [t1].[Id0] +FROM ( + SELECT TOP(2) [u].[Id] + FROM [Users] AS [u] +) AS [t] +OUTER APPLY ( + SELECT [t0].[UserRate] AS [Rate], [t0].[UserTextRate] AS [UserRateText], [u0].[Id] AS [UserId], [t0].[Id], [t0].[Id0], [t0].[DateArrived] + FROM ( + SELECT TOP(12) [c].[Id], [t2].[Id] AS [Id0], [t2].[DateArrived], [t2].[UserEUserId], [t2].[UserRate], [t2].[UserTextRate] + FROM [Cars] AS [c] + INNER JOIN [Taxis] AS [t2] ON [c].[Id] = [t2].[CarId] + WHERE [t].[Id] = [c].[EUserId] + ORDER BY [t2].[DateArrived] DESC + ) AS [t0] + LEFT JOIN [Users] AS [u0] ON [t0].[UserEUserId] = [u0].[Id] +) AS [t1] +ORDER BY [t].[Id], [t1].[DateArrived] DESC, [t1].[Id], [t1].[Id0] +"""); } - private JetTestStore CreateDatabase12456() - => CreateTestStore( - () => new MyContext12456(_options), - context => - { - ClearLog(); - }); - - private class CompetitionSeason12456 + private class EUser { public int Id { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } - public List ActivityTypePoints { get; set; } + + public ICollection Cars { get; set; } } - private class Point12456 + private class Taxi { public int Id { get; set; } - public CompetitionSeason12456 CompetitionSeason { get; set; } - public int? Points { get; set; } + public DateTime? DateArrived { get; set; } + public int? UserRate { get; set; } + public string UserTextRate { get; set; } + public string ReportText { get; set; } + public EUser UserEUser { get; set; } } - private class ActivityType12456 + private class Car { public int Id { get; set; } - public List Points { get; set; } + public ICollection Taxis { get; set; } } - private class ActivityTypePoints12456 + private class MyContext19947 : DbContext { - public int Id { get; set; } - public int ActivityTypeId { get; set; } - public int CompetitionSeasonId { get; set; } - public int Points { get; set; } + public MyContext19947(DbContextOptions options) + : base(options) + { + } - public ActivityType12456 ActivityType { get; set; } - public CompetitionSeason12456 CompetitionSeason { get; set; } - } + public DbSet Users { get; set; } + public DbSet Cars { get; set; } + public DbSet Taxis { get; set; } - private class Activity12456 - { - public int Id { get; set; } - public int ActivityTypeId { get; set; } - public DateTime DateTime { get; set; } - public int? Points { get; set; } - public ActivityType12456 ActivityType { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } } #endregion - #region Issue15137 + #region Issue20813 [ConditionalFact] - public virtual async Task Run_something() + public virtual async Task SelectMany_and_collection_in_projection_in_FirstOrDefault() { - using (CreateDatabase15137()) - { - using (var context = new MyContext15137(_options)) - { - var container = await context.Trades - .Select( - x => new + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + var referenceId = "a"; + var customerId = new Guid("1115c816-6c4c-4016-94df-d8b60a22ffa1"); + var query = context.Orders + .Where(o => o.ExternalReferenceId == referenceId && o.CustomerId == customerId) + .Select( + o => new + { + IdentityDocuments = o.IdentityDocuments.Select( + id => new { - x.Id, - Assets = x.Assets.AsQueryable() - .Select( - y => new - { - y.Id, - Contract = new - { - y.Contract.Id, - Season = new - { - y.Contract.Season.Id, - IsPastTradeDeadline = - (y.Contract.Season.Games.Max(z => (int?)z.GameNumber) ?? 0) > 10 - } - } - }) - .ToList() + Images = o.IdentityDocuments + .SelectMany(id => id.Images) + .Select(i => new { i.Image }), }) - .SingleAsync(); + }).SingleOrDefault(); - AssertSql( - $@"SELECT `t0`.`Id`, `t1`.`Id`, `t1`.`Id0`, `t1`.`Id1`, `t1`.`c` + // Verify the valid generated SQL + AssertSql( + """ +@__referenceId_0='a' (Size = 4000) +@__customerId_1='1115c816-6c4c-4016-94df-d8b60a22ffa1' + +SELECT [t].[Id], [t0].[Id], [t0].[Image], [t0].[Id0], [t0].[Id00] FROM ( - SELECT TOP 2 `t`.`Id` - FROM `Trades` AS `t` -) AS `t0` -LEFT JOIN ( - SELECT `d0`.`Id`, `d1`.`Id` AS `Id0`, `d2`.`Id` AS `Id1`, CASE - WHEN COALESCE(( - SELECT MAX(`d`.`GameNumber`) - FROM `DbGame` AS `d` - WHERE `d2`.`Id` IS NOT NULL AND (`d2`.`Id` = `d`.`SeasonId`)), 0) > 10 THEN True - ELSE False - END AS `c`, `d0`.`DbTradeId` - FROM `DbTradeAsset` AS `d0` - INNER JOIN `DbContract` AS `d1` ON `d0`.`ContractId` = `d1`.`Id` - LEFT JOIN `DbSeason` AS `d2` ON `d1`.`SeasonId` = `d2`.`Id` -) AS `t1` ON `t0`.`Id` = `t1`.`DbTradeId` -ORDER BY `t0`.`Id`, `t1`.`Id`, `t1`.`Id0`"); - } + SELECT TOP(2) [o].[Id] + FROM [Orders] AS [o] + WHERE [o].[ExternalReferenceId] = @__referenceId_0 AND [o].[CustomerId] = @__customerId_1 +) AS [t] +OUTER APPLY ( + SELECT [i].[Id], [t1].[Image], [t1].[Id] AS [Id0], [t1].[Id0] AS [Id00] + FROM [IdentityDocument] AS [i] + OUTER APPLY ( + SELECT [i1].[Image], [i0].[Id], [i1].[Id] AS [Id0] + FROM [IdentityDocument] AS [i0] + INNER JOIN [IdentityDocumentImage] AS [i1] ON [i0].[Id] = [i1].[IdentityDocumentId] + WHERE [t].[Id] = [i0].[OrderId] + ) AS [t1] + WHERE [t].[Id] = [i].[OrderId] +) AS [t0] +ORDER BY [t].[Id], [t0].[Id], [t0].[Id0] +"""); + } + + private class Order + { + private ICollection _identityDocuments; + + public Guid Id { get; set; } + + public Guid CustomerId { get; set; } + + public string ExternalReferenceId { get; set; } + + public ICollection IdentityDocuments + { + get => _identityDocuments = _identityDocuments ?? new Collection(); + set => _identityDocuments = value; } } - private class MyContext15137 : DbContext + private class IdentityDocument { - public DbSet Trades { get; set; } + private ICollection _images; - public MyContext15137(DbContextOptions options) + public Guid Id { get; set; } + + [ForeignKey(nameof(Order))] + public Guid OrderId { get; set; } + + public Order Order { get; set; } + + public ICollection Images + { + get => _images = _images ?? new Collection(); + set => _images = value; + } + } + + private class IdentityDocumentImage + { + public Guid Id { get; set; } + + [ForeignKey(nameof(IdentityDocument))] + public Guid IdentityDocumentId { get; set; } + + public byte[] Image { get; set; } + + public IdentityDocument IdentityDocument { get; set; } + } + + private class MyContext20813 : DbContext + { + public MyContext20813(DbContextOptions options) : base(options) { } + + public DbSet Orders { get; set; } } - private JetTestStore CreateDatabase15137() - => CreateTestStore( - () => new MyContext15137(_options), - context => - { - var dbTrade = new DbTrade - { - Assets = new List - { - new DbTradeAsset - { - Contract = new DbContract - { - Season = new DbSeason { Games = new List { new DbGame { GameNumber = 1 } } } - } - } - } - }; + #endregion - context.Trades.Add(dbTrade); - context.SaveChanges(); + #region Issue18738 - ClearLog(); - }); + [ConditionalFact] + public virtual async Task Set_operation_in_pending_collection() + { + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + var resultCollection = context.StudentGameMapper + .OrderBy(s => s.Id) + .Select( + s => new StudentGameResult + { + SportsList = ( + from inDoorSports in context.InDoorSports + where inDoorSports.Id == s.InCategoryId + select inDoorSports.Name) + .Union( + from outDoorSports in context.OutDoorSports + where outDoorSports.Id == s.OutCategoryId + select outDoorSports.Name) + .ToList() + }) + .Take(5) // Without this line the query works + .ToList(); + + // Verify the valid generated SQL + AssertSql( + """ +@__p_0='5' + +SELECT [t].[Id], [t0].[Name] +FROM ( + SELECT TOP(@__p_0) [s].[Id], [s].[InCategoryId], [s].[OutCategoryId] + FROM [StudentGameMapper] AS [s] + ORDER BY [s].[Id] +) AS [t] +OUTER APPLY ( + SELECT [i].[Name] + FROM [InDoorSports] AS [i] + WHERE [i].[Id] = [t].[InCategoryId] + UNION + SELECT [o].[Name] + FROM [OutDoorSports] AS [o] + WHERE [o].[Id] = [t].[OutCategoryId] +) AS [t0] +ORDER BY [t].[Id] +"""); + } - private class DbTrade + private class StudentGameMapper { public int Id { get; set; } - public List Assets { get; set; } + public int InCategoryId { get; set; } + public int OutCategoryId { get; set; } } - private class DbTradeAsset + private class InDoorSports { public int Id { get; set; } - public int ContractId { get; set; } - - public DbContract Contract { get; set; } + public string Name { get; set; } } - private class DbContract + private class OutDoorSports { public int Id { get; set; } - - public DbSeason Season { get; set; } + public string Name { get; set; } } - private class DbSeason + private class StudentGameResult { - public int Id { get; set; } - - public List Games { get; set; } + public int GroupId { get; set; } + public int StudentId { get; set; } + public List SportsList { get; set; } } - private class DbGame + private class MyContext18738 : DbContext { - public int Id { get; set; } - public int GameNumber { get; set; } + public MyContext18738(DbContextOptions options) + : base(options) + { + } - public DbSeason Season { get; set; } + public DbSet StudentGameMapper { get; set; } + public DbSet InDoorSports { get; set; } + public DbSet OutDoorSports { get; set; } } #endregion - #region Issue13517 + #region Issue24216 [ConditionalFact] - public void Query_filter_with_pk_fk_optimization_bug_13517() - { - using var _ = CreateDatabase13517(); - using var context = new BugContext13517(_options); - - context.Entities.Select( - s => - new BugEntityDto13517 - { - Id = s.Id, - RefEntity = s.RefEntity == null - ? null - : new BugRefEntityDto13517 { Id = s.RefEntity.Id, Public = s.RefEntity.Public }, - RefEntityId = s.RefEntityId - }).Single(p => p.Id == 1); - + public virtual async Task Subquery_take_SelectMany_with_TVF() + { + var contextFactory = await InitializeAsync(); + + using var context = contextFactory.CreateContext(); + + context.Database.ExecuteSqlRaw( + @"create function [dbo].[GetPersonStatusAsOf] (@personId bigint, @timestamp datetime2) + returns @personStatus table + ( + Id bigint not null, + PersonId bigint not null, + GenderId bigint not null, + StatusMessage nvarchar(max) + ) + as + begin + insert into @personStatus + select [m].[Id], [m].[PersonId], [m].[PersonId], null + from [Message] as [m] + where [m].[PersonId] = @personId and [m].[TimeStamp] = @timestamp + return + end"); + + ClearLog(); + + var q = from m in context.Message + orderby m.Id + select m; + + var q2 = + from m in q.Take(10) + from asof in context.GetPersonStatusAsOf(m.PersonId, m.Timestamp) + select new { Gender = (from g in context.Gender where g.Id == asof.GenderId select g.Description).Single() }; + + q2.ToList(); + + // Verify the valid generated SQL AssertSql( - $@"SELECT TOP 2 `e`.`Id`, IIF(`t`.`Id` IS NULL, 1, 0), `t`.`Id`, `t`.`Public`, `e`.`RefEntityId` -FROM `Entities` AS `e` -LEFT JOIN ( - SELECT `r`.`Id`, `r`.`Public` - FROM `RefEntities` AS `r` - WHERE `r`.`Public` = True -) AS `t` ON `e`.`RefEntityId` = `t`.`Id` -WHERE `e`.`Id` = 1"); - } - - private JetTestStore CreateDatabase13517() - => CreateTestStore( - () => new BugContext13517(_options), - context => - { - var refEntity = new BugRefEntity13517 { Public = false }; - context.RefEntities.Add(refEntity); - context.Entities.Add(new BugEntity13517 { RefEntity = refEntity }); - context.SaveChanges(); - - ClearLog(); - }); + """ +@__p_0='10' - private class BugEntity13517 - { - public int Id { get; set; } - public int? RefEntityId { get; set; } - public BugRefEntity13517 RefEntity { get; set; } +SELECT ( + SELECT TOP(1) [g0].[Description] + FROM [Gender] AS [g0] + WHERE [g0].[Id] = [g].[GenderId]) AS [Gender] +FROM ( + SELECT TOP(@__p_0) [m].[Id], [m].[PersonId], [m].[Timestamp] + FROM [Message] AS [m] + ORDER BY [m].[Id] +) AS [t] +CROSS APPLY [dbo].[GetPersonStatusAsOf]([t].[PersonId], [t].[Timestamp]) AS [g] +ORDER BY [t].[Id] +"""); } - private class BugRefEntity13517 + private class Gender { - public int Id { get; set; } - public bool Public { get; set; } + public long Id { get; set; } + + public string Description { get; set; } } - private class BugEntityDto13517 + private class Message { - public int Id { get; set; } - public int? RefEntityId { get; set; } - public BugRefEntityDto13517 RefEntity { get; set; } + public long Id { get; set; } + + public long PersonId { get; set; } + + public DateTime Timestamp { get; set; } } - private class BugRefEntityDto13517 + private class PersonStatus { - public int Id { get; set; } - public bool Public { get; set; } + public long Id { get; set; } + + public long PersonId { get; set; } + + public long GenderId { get; set; } + + public string StatusMessage { get; set; } } - private class BugContext13517 : DbContext + private class MyContext24216 : DbContext { - public DbSet Entities { get; set; } - public DbSet RefEntities { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) + public MyContext24216(DbContextOptions options) + : base(options) { - modelBuilder.Entity().HasQueryFilter(f => f.Public); } - public BugContext13517(DbContextOptions options) - : base(options) + public DbSet Gender { get; set; } + + public DbSet Message { get; set; } + + public IQueryable GetPersonStatusAsOf(long personId, DateTime asOf) + => FromExpression(() => GetPersonStatusAsOf(personId, asOf)); + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + base.OnModelCreating(modelBuilder); + + modelBuilder.HasDbFunction( + typeof(MyContext24216).GetMethod( + nameof(GetPersonStatusAsOf), + new[] { typeof(long), typeof(DateTime) })); } } #endregion - #region Issue17794 + #region Issue23198 [ConditionalFact] - public void Double_convert_interface_created_expression_tree() + public virtual void An_optional_dependent_without_any_columns_and_nested_dependent_throws() { - using var _ = CreateDatabase17794(); - using var context = new BugContext17794(_options); - - var expression = HasAction17794(OfferActions17794.Accepted); - var query = context.Offers.Where(expression).Count(); + using var context = new MyContext23198(); - Assert.Equal(1, query); - - AssertSql( - $@"{AssertSqlHelper.Declaration("@__action_0='1'")} - -SELECT COUNT(*) -FROM `Offers` AS `o` -WHERE EXISTS ( - SELECT 1 - FROM `OfferActions` AS `o0` - WHERE (`o`.`Id` = `o0`.`OfferId`) AND (`o0`.`Action` = {AssertSqlHelper.Parameter("@__action_0")}))"); + Assert.Equal( + RelationalStrings.OptionalDependentWithDependentWithoutIdentifyingProperty(nameof(AnOwnedTypeWithOwnedProperties)), + Assert.Throws(() => context.Model).Message); } - private JetTestStore CreateDatabase17794() - => CreateTestStore( - () => new BugContext17794(_options), - context => - { - context.Add(new Offer17794 + private class MyContext23198 : DbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseJet(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().OwnsOne( + e => e.AnOwnedTypeWithOwnedProperties, + b => { - Actions = new List - { - new OfferAction17794 - { - Action = OfferActions17794.Accepted - } - } + b.OwnsOne(e => e.AnOwnedTypeWithPrimitiveProperties1); + b.OwnsOne(e => e.AnOwnedTypeWithPrimitiveProperties2); }); + } - context.SaveChanges(); - - ClearLog(); - }); - - private static Expression> HasAction17794(OfferActions17794 action) - where T : IOffer17794 + public class AnAggregateRoot { - Expression> predicate = oa => oa.Action == action; - - return v => v.Actions.AsQueryable().Any(predicate); + public string Id { get; set; } + public AnOwnedTypeWithOwnedProperties AnOwnedTypeWithOwnedProperties { get; set; } } - private interface IOffer17794 + public class AnOwnedTypeWithOwnedProperties { - ICollection Actions { get; set; } + public AnOwnedTypeWithPrimitiveProperties1 AnOwnedTypeWithPrimitiveProperties1 { get; set; } + public AnOwnedTypeWithPrimitiveProperties2 AnOwnedTypeWithPrimitiveProperties2 { get; set; } } - private class Offer17794 : IOffer17794 + public class AnOwnedTypeWithPrimitiveProperties1 { - public int Id { get; set; } - - public ICollection Actions { get; set; } + public string Name { get; set; } } - private enum OfferActions17794 : int + public class AnOwnedTypeWithPrimitiveProperties2 { - Accepted = 1, - Declined = 2 + public string Name { get; set; } } - private class OfferAction17794 + #endregion + + #region Issue24569 + + // TODO: Remove when JSON is first class and we have proper tests. See issue#4021 + + [ConditionalFact] + public virtual async Task Builtin_tvf_translated_correctly() { - public int Id { get; set; } + var contextFactory = await InitializeAsync(seed: c => c.Seed()); - [Required] - public Offer17794 Offer { get; set; } - public int OfferId { get; set; } + using (var context = contextFactory.CreateContext()) + { + var query = await (from c in context.Cars + from j in context.OpenJson(c.Json, "$.items") + select new { c, j }).ToListAsync(); - [Required] - public OfferActions17794 Action { get; set; } + AssertSql( + """ +SELECT [c].[Id], [c].[Json], [o].[Value] +FROM [Cars] AS [c] +CROSS APPLY OPENJSON([c].[Json], N'$.items') AS [o] +"""); + } } - private class BugContext17794 : DbContext + protected class MyContext24569 : DbContext { - public DbSet Offers { get; set; } - public DbSet OfferActions { get; set; } + public DbSet Cars { get; set; } + + public MyContext24569(DbContextOptions options) + : base(options) + { + } protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.HasDbFunction(() => OpenJson(string.Empty, string.Empty)).HasStoreType("nvarchar(max)"); + + [DbFunction("OPENJSON", IsBuiltIn = true)] + public IQueryable OpenJson(string column, string jsonPath) + => FromExpression(() => OpenJson(column, jsonPath)); + + public void Seed() { + Cars.Add(new Car24569 { Json = @"{ ""name"": ""test"", ""items"": [{""id"": 1}, {""id"": 2}] }", }); + SaveChanges(); } - public BugContext17794(DbContextOptions options) - : base(options) + public class Car24569 + { + public int Id { get; set; } + + public string Json { get; set; } + } + + [Keyless] + public class JsonResult { + public string Value { get; set; } } } #endregion - #region Issue18087 + #region Issue25400 - [ConditionalFact] - public void Cast_to_implemented_interface_is_removed_from_expression_tree() + [ConditionalTheory] + [InlineData(true)] + [InlineData(false)] + public virtual async Task NoTracking_split_query_creates_only_required_instances(bool async) { - using var _ = CreateDatabase18087(); - using var context = new BugContext18087(_options); + var contextFactory = await InitializeAsync( + seed: c => c.Seed(), + onConfiguring: o => new JetDbContextOptionsBuilder(o).UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); - var queryBase = (IQueryable)context.MockEntities; - var id = 1; - var query = queryBase.Cast().FirstOrDefault(x => x.Id == id); + using (var context = contextFactory.CreateContext()) + { + Test25400.ConstructorCallCount = 0; - Assert.Equal(1, query.Id); + var query = context.Set().AsNoTracking().OrderBy(e => e.Id); + var test = async + ? await query.FirstOrDefaultAsync() + : query.FirstOrDefault(); - AssertSql( - $@"{AssertSqlHelper.Declaration("@__id_0='1'")} + Assert.Equal(1, Test25400.ConstructorCallCount); -SELECT TOP 1 `m`.`Id`, `m`.`Name`, `m`.`NavigationEntityId` -FROM `MockEntities` AS `m` -WHERE `m`.`Id` = {AssertSqlHelper.Parameter("@__id_0")}"); + AssertSql( +""" +SELECT TOP 1 `t`.`Id`, `t`.`Value` +FROM `Tests` AS `t` +ORDER BY `t`.`Id` +"""); + } } - [ConditionalFact] - public void Cast_to_object_is_removed_from_expression_tree() + protected class MyContext25400 : DbContext { - using var _ = CreateDatabase18087(); - using var context = new BugContext18087(_options); + public DbSet Tests { get; set; } + + public MyContext25400(DbContextOptions options) + : base(options) + { + } - var queryBase = (IQueryable)context.MockEntities; - var query = queryBase.Cast().Count(); + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasKey(e => e.Id); - Assert.Equal(3, query); + public void Seed() + { + Tests.Add(new Test25400(15)); - AssertSql( - $@"SELECT COUNT(*) -FROM `MockEntities` AS `m`"); + SaveChanges(); + } } - [ConditionalFact] - public void Cast_to_non_implemented_interface_is_not_removed_from_expression_tree() + protected class Test25400 { - using var _ = CreateDatabase18087(); - using var context = new BugContext18087(_options); + public static int ConstructorCallCount; - var queryBase = (IQueryable)context.MockEntities; - var id = 1; + public Test25400() + { + ++ConstructorCallCount; + } - var message = Assert.Throws( - () => queryBase.Cast().FirstOrDefault(x => x.Id == id)).Message; + public Test25400(int value) + { + Value = value; + } - Assert.Equal( - CoreStrings.TranslationFailed(@"DbSet .Cast() .Where(e => e.Id == __id_0)"), - message.Replace("\r", "").Replace("\n", "")); + public int Id { get; set; } + public int Value { get; set; } } - private JetTestStore CreateDatabase18087() - => CreateTestStore( - () => new BugContext18087(_options), - context => - { - context.AddRange(new MockEntity() - { - Name = "Entity1", - NavigationEntity = null - }, - new MockEntity() - { - Name = "Entity2", - NavigationEntity = null - }, - new MockEntity() - { - Name = "NewEntity", - NavigationEntity = null - }); - - context.SaveChanges(); + #endregion - ClearLog(); - }); + #region Issue25225 - private interface IDomainEntity + [ConditionalFact] + public virtual async Task Can_query_with_nav_collection_in_projection_with_split_query_in_parallel_async() { - int Id { get; set; } + var contextFactory = await CreateContext25225Async(); + var task1 = QueryAsync(MyContext25225.Parent1Id, MyContext25225.Collection1Id); + var task2 = QueryAsync(MyContext25225.Parent2Id, MyContext25225.Collection2Id); + await Task.WhenAll(task1, task2); + + async Task QueryAsync(Guid parentId, Guid collectionId) + { + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + for (var i = 0; i < 100; i++) + { + var parent = await SelectParent25225(context, parentId).SingleAsync(); + AssertParent25225(parentId, collectionId, parent); + } + } + } } - private interface IDummyEntity + [ConditionalFact] + public virtual async Task Can_query_with_nav_collection_in_projection_with_split_query_in_parallel_sync() { - int Id { get; set; } + var contextFactory = await CreateContext25225Async(); + var task1 = Task.Run(() => Query(MyContext25225.Parent1Id, MyContext25225.Collection1Id)); + var task2 = Task.Run(() => Query(MyContext25225.Parent2Id, MyContext25225.Collection2Id)); + await Task.WhenAll(task1, task2); + + void Query(Guid parentId, Guid collectionId) + { + using (var context = contextFactory.CreateContext()) + { + ClearLog(); + for (var i = 0; i < 10; i++) + { + var parent = SelectParent25225(context, parentId).Single(); + AssertParent25225(parentId, collectionId, parent); + } + } + } } - private class MockEntity : IDomainEntity - { - public int Id { get; set; } - public string Name { get; set; } + private Task> CreateContext25225Async() + => InitializeAsync( + seed: c => c.Seed(), + onConfiguring: o => new JetDbContextOptionsBuilder(o).UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) + ); + + private static IQueryable SelectParent25225(MyContext25225 context, Guid parentId) + => context + .Parents + .Where(x => x.Id == parentId) + .Select( + p => new ParentViewModel25225 + { + Id = p.Id, + Collection = p + .Collection + .Select( + c => new CollectionViewModel25225 + { + Id = c.Id, + ParentId = c.ParentId, + }) + .ToArray() + }); - public MockEntity NavigationEntity { get; set; } + private static void AssertParent25225(Guid expectedParentId, Guid expectedCollectionId, ParentViewModel25225 actualParent) + { + Assert.Equal(expectedParentId, actualParent.Id); + Assert.Collection( + actualParent.Collection, + c => Assert.Equal(expectedCollectionId, c.Id) + ); } - private class BugContext18087 : DbContext + protected class MyContext25225 : DbContext { - public BugContext18087(DbContextOptions options) + public static readonly Guid Parent1Id = new("d6457b52-690a-419e-8982-a1a8551b4572"); + public static readonly Guid Parent2Id = new("e79c82f4-3ae7-4c65-85db-04e08cba6fa7"); + public static readonly Guid Collection1Id = new("7ce625fb-863d-41b3-b42e-e4e4367f7548"); + public static readonly Guid Collection2Id = new("d347bbd5-003a-441f-a148-df8ab8ac4a29"); + public DbSet Parents { get; set; } + + public MyContext25225(DbContextOptions options) : base(options) { } - public DbSet MockEntities { get; set; } + public void Seed() + { + var parent1 = new Parent25225 { Id = Parent1Id, Collection = new List { new() { Id = Collection1Id, } } }; + + var parent2 = new Parent25225 { Id = Parent2Id, Collection = new List { new() { Id = Collection2Id, } } }; + + AddRange(parent1, parent2); + + SaveChanges(); + } + + public class Parent25225 + { + public Guid Id { get; set; } + public ICollection Collection { get; set; } + } + + public class Collection25225 + { + public Guid Id { get; set; } + public Guid ParentId { get; set; } + public Parent25225 Parent { get; set; } + } + } + + public class ParentViewModel25225 + { + public Guid Id { get; set; } + public ICollection Collection { get; set; } + } + + public class CollectionViewModel25225 + { + public Guid Id { get; set; } + public Guid ParentId { get; set; } } #endregion - #region Issue18759 + #region Issue26742 - [ConditionalFact] - public void Query_filter_with_null_constant() - { - using var _ = CreateDatabase18759(); - using var context = new BugContext18759(_options); + [ConditionalTheory] + [InlineData(null, "")] + //[InlineData(0, " (Scale = 0)")] //https://github.com/dotnet/SqlClient/issues/1380 cause this test to fail, not EF + [InlineData(1, " (Scale = 1)")] + [InlineData(2, " (Scale = 2)")] + [InlineData(3, " (Scale = 3)")] + [InlineData(4, " (Scale = 4)")] + [InlineData(5, " (Scale = 5)")] + [InlineData(6, " (Scale = 6)")] + [InlineData(7, " (Scale = 7)")] + public virtual async Task Query_generates_correct_datetime2_parameter_definition(int? fractionalSeconds, string postfix) + { + var contextFactory = await InitializeAsync( + onModelCreating: modelBuilder => + { + if (fractionalSeconds.HasValue) + { + modelBuilder.Entity().Property(p => p.DateTime).HasPrecision(fractionalSeconds.Value); + } + }); - var people = context.People.ToList(); + var parameter = new DateTime(2021, 11, 12, 13, 14, 15).AddTicks(1234567); - AssertSql( - $@"SELECT `p`.`Id`, `p`.`UserDeleteId` -FROM `People` AS `p` -LEFT JOIN `User18759` AS `u` ON `p`.`UserDeleteId` = `u`.`Id` -WHERE `u`.`Id` IS NOT NULL"); + using (var context = contextFactory.CreateContext()) + { + _ = context.Entities.Where(x => x.DateTime == parameter).Select(e => e.DateTime).FirstOrDefault(); + + AssertSql( +""" +@__parameter_0='2021-11-12T13:14:15.0000000' (DbType = DateTime) + +SELECT TOP 1 `e`.`DateTime` +FROM `Entities` AS `e` +WHERE `e`.`DateTime` = CDATE(@__parameter_0) +"""); + } } - private JetTestStore CreateDatabase18759() - => CreateTestStore( - () => new BugContext18759(_options), - context => - { - ClearLog(); + [ConditionalTheory] + [InlineData(null, "")] + //[InlineData(0, " (Scale = 0)")] //https://github.com/dotnet/SqlClient/issues/1380 cause this test to fail, not EF + [InlineData(1, " (Scale = 1)")] + [InlineData(2, " (Scale = 2)")] + [InlineData(3, " (Scale = 3)")] + [InlineData(4, " (Scale = 4)")] + [InlineData(5, " (Scale = 5)")] + [InlineData(6, " (Scale = 6)")] + [InlineData(7, " (Scale = 7)")] + public virtual async Task Query_generates_correct_datetimeoffset_parameter_definition(int? fractionalSeconds, string postfix) + { + var contextFactory = await InitializeAsync( + onModelCreating: modelBuilder => + { + if (fractionalSeconds.HasValue) + { + modelBuilder.Entity().Property(p => p.DateTimeOffset).HasPrecision(fractionalSeconds.Value); + } }); - public class Person18759 - { - public int Id { get; set; } - public User18759 UserDelete { get; set; } + var parameter = new DateTimeOffset(new DateTime(2021, 11, 12, 13, 14, 15).AddTicks(1234567), TimeSpan.FromHours(10)); + + using (var context = contextFactory.CreateContext()) + { + _ = context.Entities.Where(x => x.DateTimeOffset == parameter).Select(e => e.DateTimeOffset).FirstOrDefault(); + + AssertSql( +""" +@__parameter_0='2021-11-12T13:14:15.1234567+10:00' (Nullable = false) (Size = 33) + +SELECT TOP 1 `e`.`DateTimeOffset` +FROM `Entities` AS `e` +WHERE `e`.`DateTimeOffset` = @__parameter_0 +"""); + } } - public class User18759 - { - public int Id { get; set; } + [ConditionalTheory] + [InlineData(null, "")] + //[InlineData(0, " (Scale = 0)")] //https://github.com/dotnet/SqlClient/issues/1380 cause this test to fail, not EF + [InlineData(1, " (Scale = 1)")] + [InlineData(2, " (Scale = 2)")] + [InlineData(3, " (Scale = 3)")] + [InlineData(4, " (Scale = 4)")] + [InlineData(5, " (Scale = 5)")] + [InlineData(6, " (Scale = 6)")] + [InlineData(7, " (Scale = 7)")] + public virtual async Task Query_generates_correct_timespan_parameter_definition(int? fractionalSeconds, string postfix) + { + var contextFactory = await InitializeAsync( + onModelCreating: modelBuilder => + { + if (fractionalSeconds.HasValue) + { + modelBuilder.Entity().Property(p => p.TimeSpan).HasPrecision(fractionalSeconds.Value); + } + }); + + var parameter = TimeSpan.Parse("12:34:56.7890123", CultureInfo.InvariantCulture); + + using (var context = contextFactory.CreateContext()) + { + _ = context.Entities.Where(x => x.TimeSpan == parameter).Select(e => e.TimeSpan).FirstOrDefault(); + + AssertSql( +""" +@__parameter_0='12:34:56.7890123' + +SELECT TOP 1 `e`.`TimeSpan` +FROM `Entities` AS `e` +WHERE `e`.`TimeSpan` = @__parameter_0 +"""); + } } - private class BugContext18759 : DbContext + protected class MyContext_26742 : DbContext { - public DbSet People { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - => modelBuilder.Entity().HasQueryFilter(p => p.UserDelete != null); + public DbSet Entities { get; set; } - public BugContext18759(DbContextOptions options) + public MyContext_26742(DbContextOptions options) : base(options) { } + + public class Entity + { + public int Id { get; set; } + public TimeSpan TimeSpan { get; set; } + public DateTime DateTime { get; set; } + public DateTimeOffset DateTimeOffset { get; set; } + } } - #endregion Issue18759 + #endregion + + protected override string StoreName + => "QueryBugsTest"; - private DbContextOptions _options; + protected TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; - private JetTestStore CreateTestStore( - Func contextCreator, - Action contextInitializer) - where TContext : DbContext, IDisposable - { - var testStore = JetTestStore.CreateInitialized("QueryBugsTest"); + protected override ITestStoreFactory TestStoreFactory + => JetTestStoreFactory.Instance; + + protected override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).ConfigureWarnings( + w => + { + w.Log(JetEventId.ByteIdentityColumnWarning); + w.Log(JetEventId.DecimalTypeKeyWarning); + }); + + protected override TestStore CreateTestStore() + => JetTestStore.CreateInitialized(StoreName); - _options = Fixture.CreateOptions(testStore); + private static readonly FieldInfo _querySplittingBehaviorFieldInfo = + typeof(RelationalOptionsExtension).GetField("_querySplittingBehavior", BindingFlags.NonPublic | BindingFlags.Instance); - using (var context = contextCreator()) + protected DbContextOptionsBuilder ClearQuerySplittingBehavior(DbContextOptionsBuilder optionsBuilder) + { + var extension = optionsBuilder.Options.FindExtension(); + if (extension == null) + { + extension = new JetOptionsExtension(); + } + else { - context.Database.EnsureCreatedResiliently(); - contextInitializer?.Invoke(context); + _querySplittingBehaviorFieldInfo.SetValue(extension, null); } - return testStore; + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + + return optionsBuilder; } protected void ClearLog() - { - Fixture.TestSqlLoggerFactory.Clear(); - } + => TestSqlLoggerFactory.Clear(); - private void AssertSql(params string[] expected) - { - Fixture.TestSqlLoggerFactory.AssertBaseline(expected); - } + protected void AssertSql(params string[] expected) + => TestSqlLoggerFactory.AssertBaseline(expected); } } diff --git a/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestHelpers.cs b/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestHelpers.cs index 6233e53..db0b517 100644 --- a/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestHelpers.cs +++ b/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestHelpers.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection; namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities { - public class JetTestHelpers : TestHelpers + public class JetTestHelpers : RelationalTestHelpers { protected JetTestHelpers() { diff --git a/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestStore.cs b/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestStore.cs index 637ec41..94af385 100644 --- a/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestStore.cs +++ b/test/EFCore.Jet.FunctionalTests/TestUtilities/JetTestStore.cs @@ -159,7 +159,13 @@ namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities } public void DeleteDatabase() - => JetConnection.DropDatabase(CreateConnectionString(Name)); + { + if (ConnectionState != ConnectionState.Closed) + { + CloseConnection(); + } + JetConnection.DropDatabase(CreateConnectionString(Name)); + } public override void OpenConnection() => new TestJetRetryingExecutionStrategy().Execute(Connection, connection => connection.Open());