diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs index 98f4a1f9c..0bce1baf8 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs @@ -2,7 +2,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates; public class NonSharedModelBulkUpdatesNpgsqlTest(NonSharedFixture fixture) : NonSharedModelBulkUpdatesRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; @@ -149,7 +149,7 @@ public override async Task Update_owned_and_non_owned_properties_with_table_shar public override async Task Update_main_table_in_entity_with_entity_splitting(bool async) { // Overridden/duplicated because we update DateTime, which Npgsql requires to be a UTC timestamp - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( onModelCreating: mb => mb.Entity() .ToTable("Blogs") .SplitToTable( @@ -166,7 +166,7 @@ public override async Task Update_main_table_in_entity_with_entity_splitting(boo await AssertUpdate( async, - contextFactory.CreateContext, + contextFactory.CreateDbContext, ss => ss.Set(), s => s.SetProperty(b => b.CreationTimestamp, b => new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc)), rowsAffectedCount: 1); @@ -226,7 +226,7 @@ SELECT COALESCE(sum(o0."Amount"), 0)::int [MemberData(nameof(IsAsyncData))] public virtual async Task Update_with_primitive_collection_in_value_selector(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( seed: async ctx => { ctx.AddRange(new EntityWithPrimitiveCollection { Tags = ["tag1", "tag2"] }); @@ -235,7 +235,7 @@ public virtual async Task Update_with_primitive_collection_in_value_selector(boo await Assert.ThrowsAsync(() => AssertUpdate( async, - contextFactory.CreateContext, + contextFactory.CreateDbContext, ss => ss.EntitiesWithPrimitiveCollection, s => s.SetProperty(x => x.Tags, x => x.Tags.Append("another_tag")), rowsAffectedCount: 1)); diff --git a/test/EFCore.PG.FunctionalTests/EntitySplittingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/EntitySplittingNpgsqlTest.cs index def3b2028..823bc2d01 100644 --- a/test/EFCore.PG.FunctionalTests/EntitySplittingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/EntitySplittingNpgsqlTest.cs @@ -3,6 +3,6 @@ namespace Microsoft.EntityFrameworkCore; public class EntitySplittingNpgsqlTest(NonSharedFixture fixture, ITestOutputHelper testOutputHelper) : EntitySplittingTestBase(fixture, testOutputHelper) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs index 9c68c0d66..d6b4e5c31 100644 --- a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs @@ -563,7 +563,7 @@ protected class LogSequenceNumberType public NpgsqlLogSequenceNumber LogSequenceNumber { get; set; } } - protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; protected override IServiceCollection AddServices(IServiceCollection serviceCollection) => serviceCollection.AddEntityFrameworkNpgsqlNetTopologySuite(); diff --git a/test/EFCore.PG.FunctionalTests/MaterializationInterceptionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/MaterializationInterceptionNpgsqlTest.cs index 48e9f5bd7..533727b41 100644 --- a/test/EFCore.PG.FunctionalTests/MaterializationInterceptionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/MaterializationInterceptionNpgsqlTest.cs @@ -16,6 +16,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocAdvancedMappingsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocAdvancedMappingsQueryNpgsqlTest.cs index 6d9c52e36..7dad01f75 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocAdvancedMappingsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocAdvancedMappingsQueryNpgsqlTest.cs @@ -3,7 +3,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocAdvancedMappingsQueryNpgsqlTest(NonSharedFixture fixture) : AdHocAdvancedMappingsQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; // Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported. diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocComplexTypeQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocComplexTypeQueryNpgsqlTest.cs index 30f940e83..f148a46e4 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocComplexTypeQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocComplexTypeQueryNpgsqlTest.cs @@ -19,6 +19,6 @@ LIMIT 2 """); } - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs index ffeb482f7..533339a39 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocJsonQueryNpgsqlTest(NonSharedFixture fixture) : AdHocJsonQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; protected override async Task Seed29219(DbContext ctx) @@ -179,7 +179,7 @@ protected override Task SeedBadJsonProperties(ContextBadJsonProperties ctx) [ConditionalTheory, MemberData(nameof(IsAsyncData))] public virtual async Task Json_predicate_on_bytea(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( seed: async context => { context.Entities.AddRange( @@ -188,7 +188,7 @@ public virtual async Task Json_predicate_on_bytea(bool async) await context.SaveChangesAsync(); }); - using (var context = contextFactory.CreateContext()) + using (var context = contextFactory.CreateDbContext()) { var query = context.Entities.Where(x => x.JsonEntity.Bytea == new byte[] { 1, 2, 4 }); @@ -211,7 +211,7 @@ LIMIT 2 [ConditionalTheory, MemberData(nameof(IsAsyncData))] public virtual async Task Json_predicate_on_interval(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( seed: async context => { context.Entities.AddRange( @@ -220,7 +220,7 @@ public virtual async Task Json_predicate_on_interval(bool async) await context.SaveChangesAsync(); }); - using (var context = contextFactory.CreateContext()) + using (var context = contextFactory.CreateDbContext()) { var query = context.Entities.Where(x => x.JsonEntity.Interval == new TimeSpan(2, 2, 3, 4, 123, 456)); diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocManyToManyQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocManyToManyQueryNpgsqlTest.cs index c06db71b4..b9527f9a2 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocManyToManyQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocManyToManyQueryNpgsqlTest.cs @@ -2,6 +2,6 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocManyToManyQueryNpgsqlTest(NonSharedFixture fixture) : AdHocManyToManyQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs index aa9f5700e..9067340a9 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs @@ -4,7 +4,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocMiscellaneousQueryNpgsqlTest(NonSharedFixture fixture) : AdHocMiscellaneousQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocNavigationsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocNavigationsQueryNpgsqlTest.cs index 023bd30ec..c1e5fd193 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocNavigationsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocNavigationsQueryNpgsqlTest.cs @@ -6,6 +6,6 @@ public class AdHocNavigationsQueryNpgsqlTest(NonSharedFixture fixture) : AdHocNa public override Task Reference_include_on_derived_type_with_sibling_works() => Assert.ThrowsAsync(() => base.Reference_include_on_derived_type_with_sibling_works()); - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocPrecompiledQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocPrecompiledQueryNpgsqlTest.cs index f593297f4..99e444ca2 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocPrecompiledQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocPrecompiledQueryNpgsqlTest.cs @@ -101,7 +101,7 @@ public override async Task Projecting_entity_with_property_requiring_converter_w public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; protected override PrecompiledQueryTestHelpers PrecompiledQueryTestHelpers diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocQueryFiltersQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocQueryFiltersQueryNpgsqlTest.cs index ac65f0f1f..7a50c2912 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocQueryFiltersQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocQueryFiltersQueryNpgsqlTest.cs @@ -3,6 +3,6 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocQueryFiltersQueryNpgsqlTest(NonSharedFixture fixture) : AdHocQueryFiltersQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocQuerySplittingQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocQuerySplittingQueryNpgsqlTest.cs index 49d04a010..a305407aa 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocQuerySplittingQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocQuerySplittingQueryNpgsqlTest.cs @@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocQuerySplittingQueryNpgsqlTest(NonSharedFixture fixture) : AdHocQuerySplittingQueryTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; private static readonly FieldInfo _querySplittingBehaviorFieldInfo = @@ -41,7 +41,7 @@ protected override DbContextOptionsBuilder ClearQuerySplittingBehavior(DbContext protected override TestStore CreateTestStore25225() { - var testStore = NpgsqlTestStore.Create(StoreName); + var testStore = NpgsqlTestStore.Create(NonSharedStoreName); testStore.UseConnectionString = true; return testStore; } diff --git a/test/EFCore.PG.FunctionalTests/Query/ArrayQueryFixture.cs b/test/EFCore.PG.FunctionalTests/Query/ArrayQueryFixture.cs index 7ea9d7176..d83218095 100644 --- a/test/EFCore.PG.FunctionalTests/Query/ArrayQueryFixture.cs +++ b/test/EFCore.PG.FunctionalTests/Query/ArrayQueryFixture.cs @@ -2,7 +2,7 @@ namespace Microsoft.EntityFrameworkCore.Query; -public abstract class ArrayQueryFixture : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory +public abstract class ArrayQueryFixture : QueryFixtureBase, ITestSqlLoggerFactory { protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; @@ -18,19 +18,16 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build protected override Task SeedAsync(ArrayQueryContext context) => ArrayQueryContext.SeedAsync(context); - public Func GetContextCreator() - => CreateContext; - - public ISetSource GetExpectedData() + public override ISetSource GetExpectedData() => _expectedData ??= new ArrayQueryData(); - public IReadOnlyDictionary EntitySorters + public override IReadOnlyDictionary EntitySorters => new Dictionary> { { typeof(ArrayEntity), e => ((ArrayEntity)e).Id }, { typeof(ArrayContainerEntity), e => ((ArrayContainerEntity)e)?.Id } }.ToDictionary(e => e.Key, e => (object)e.Value); - public IReadOnlyDictionary EntityAsserters + public override IReadOnlyDictionary EntityAsserters => new Dictionary> { { diff --git a/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs index 210303d02..f9ff56eb7 100644 --- a/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs @@ -786,6 +786,6 @@ public override async Task Tpc_entity_owning_a_split_collection_on_leaf(bool asy AssertSql(); } - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs deleted file mode 100644 index 5ea8bc486..000000000 --- a/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs +++ /dev/null @@ -1,169 +0,0 @@ -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; - -namespace Microsoft.EntityFrameworkCore.Query; - -public class NonSharedPrimitiveCollectionsQueryNpgsqlTest(NonSharedFixture fixture) - : NonSharedPrimitiveCollectionsQueryRelationalTestBase(fixture) -{ - protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) - { - new NpgsqlDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); - - return optionsBuilder; - } - - #region Support for specific element types - - // Since we just use arrays for primitive collections, there's no need to test each and every element type; arrays are fully typed - // and don't need any special conversion/handling like in providers which use JSON. - - // Npgsql maps DateTime to timestamp with time zone by default, which requires UTC timestamps. - public override Task Array_of_DateTime() - => TestArray( - new DateTime(2023, 1, 1, 12, 30, 0), - new DateTime(2023, 1, 2, 12, 30, 0), - mb => mb.Entity() - .Property(typeof(DateTime[]), "SomeArray") - .HasColumnType("timestamp without time zone[]")); - - // Npgsql maps DateTime to timestamp with time zone by default, which requires UTC timestamps. - public override Task Array_of_DateTime_with_milliseconds() - => TestArray( - new DateTime(2023, 1, 1, 12, 30, 0, 123), - new DateTime(2023, 1, 1, 12, 30, 0, 124), - mb => mb.Entity() - .Property(typeof(DateTime[]), "SomeArray") - .HasColumnType("timestamp without time zone[]")); - - // Npgsql maps DateTime to timestamp with time zone by default, which requires UTC timestamps. - public override Task Array_of_DateTime_with_microseconds() - => TestArray( - new DateTime(2023, 1, 1, 12, 30, 0, 123, 456), - new DateTime(2023, 1, 1, 12, 30, 0, 123, 457), - mb => mb.Entity() - .Property(typeof(DateTime[]), "SomeArray") - .HasColumnType("timestamp without time zone[]")); - - [ConditionalFact] - public virtual Task Array_of_DateTime_utc() - => TestArray( - new DateTime(2023, 1, 1, 12, 30, 0, DateTimeKind.Utc), - new DateTime(2023, 1, 2, 12, 30, 0, DateTimeKind.Utc)); - - // Npgsql only supports DateTimeOffset with Offset 0 (mapped to timestamp with time zone) - public override Task Array_of_DateTimeOffset() - => TestArray( - new DateTimeOffset(2023, 1, 1, 12, 30, 0, TimeSpan.Zero), - new DateTimeOffset(2023, 1, 2, 12, 30, 0, TimeSpan.Zero)); - - [ConditionalFact] - public override async Task Multidimensional_array_is_not_supported() - { - // Multidimensional arrays are supported in PostgreSQL (via the regular array type); the EFCore.PG maps .NET - // multidimensional arrays. However, arrays of multidimensional arrays aren't supported (since arrays of arrays generally aren't - // supported). - var contextFactory = await InitializeAsync( - mb => mb.Entity().Property("MultidimensionalArray"), - seed: async context => - { - var entry = context.Add(new TestEntity()); - entry.Property("MultidimensionalArray").CurrentValue = new[,] { { 1, 2 }, { 3, 4 } }; - await context.SaveChangesAsync(); - }); - - await using var context = contextFactory.CreateContext(); - - var arrays = new[] { new[,] { { 1, 2 }, { 3, 4 } }, new[,] { { 1, 2 }, { 3, 5 } } }; - - await Assert.ThrowsAsync( - () => - context.Set().Where(t => arrays.Contains(EF.Property(t, "MultidimensionalArray"))).ToArrayAsync()); - } - - #endregion Support for specific element types - - public override async Task Column_collection_inside_json_owned_entity() - { - await base.Column_collection_inside_json_owned_entity(); - - AssertSql( - """ -SELECT t."Id", t."Owned" -FROM "TestOwner" AS t -WHERE jsonb_array_length(t."Owned" -> 'Strings') = 2 -LIMIT 2 -""", - // - """ -SELECT t."Id", t."Owned" -FROM "TestOwner" AS t -WHERE (t."Owned" #>> '{Strings,1}') = 'bar' -LIMIT 2 -"""); - } - - #region Contains with various index methods - - // For Contains over column collections that have a (modeled) GIN index, we translate to the containment operator (@>). - // Otherwise we translate to the ANY construct. - [ConditionalFact] - public virtual async Task Column_collection_Contains_with_GIN_index_uses_containment() - { - var contextFactory = await InitializeAsync( - onModelCreating: mb => mb.Entity() - .HasIndex(e => e.Ints) - .HasMethod("GIN"), - seed: context => - { - context.AddRange( - new TestEntity { Id = 1, Ints = [1, 2, 3] }, - new TestEntity { Id = 2, Ints = [1, 2, 4] }); - return context.SaveChangesAsync(); - }); - - await using var context = contextFactory.CreateContext(); - - var result = await context.Set().Where(c => c.Ints!.Contains(4)).SingleAsync(); - Assert.Equal(2, result.Id); - - AssertSql( - """ -SELECT t."Id", t."Ints" -FROM "TestEntity" AS t -WHERE t."Ints" @> ARRAY[4]::integer[] -LIMIT 2 -"""); - } - - [ConditionalFact] - public virtual async Task Column_collection_Contains_with_btree_index_does_not_use_containment() - { - var contextFactory = await InitializeAsync( - onModelCreating: mb => mb.Entity().HasIndex(e => e.Ints), - seed: context => - { - context.AddRange( - new TestEntity { Id = 1, Ints = [1, 2, 3] }, - new TestEntity { Id = 2, Ints = [1, 2, 4] }); - return context.SaveChangesAsync(); - }); - - await using var context = contextFactory.CreateContext(); - - var result = await context.Set().Where(c => c.Ints!.Contains(4)).SingleAsync(); - Assert.Equal(2, result.Id); - - AssertSql( - """ -SELECT t."Id", t."Ints" -FROM "TestEntity" AS t -WHERE 4 = ANY (t."Ints") -LIMIT 2 -"""); - } - - #endregion Contains with various index methods - - protected override ITestStoreFactory TestStoreFactory - => NpgsqlTestStoreFactory.Instance; -} diff --git a/test/EFCore.PG.FunctionalTests/Query/OperatorsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/OperatorsQueryNpgsqlTest.cs index 7d453214f..f4f29a20b 100644 --- a/test/EFCore.PG.FunctionalTests/Query/OperatorsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/OperatorsQueryNpgsqlTest.cs @@ -4,7 +4,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class OperatorsQueryNpgsqlTest(NonSharedFixture fixture) : OperatorsQueryTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; protected void AssertSql(params string[] expected) @@ -152,7 +152,7 @@ LIMIT 2 [ConditionalFact] public virtual async Task AtTimeZone_and_addition() { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( seed: async context => { context.Set().AddRange( @@ -162,7 +162,7 @@ public virtual async Task AtTimeZone_and_addition() }, onModelCreating: modelBuilder => modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever()); - await using var context = contextFactory.CreateContext(); + await using var context = contextFactory.CreateDbContext(); var result = await context.Set() .Where(b => new DateOnly(2020, 1, 15) > DateOnly.FromDateTime(b.Value.AddDays(1))) diff --git a/test/EFCore.PG.FunctionalTests/Query/OwnedEntityQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/OwnedEntityQueryNpgsqlTest.cs index 3f540a3b3..bc0bc558c 100644 --- a/test/EFCore.PG.FunctionalTests/Query/OwnedEntityQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/OwnedEntityQueryNpgsqlTest.cs @@ -2,6 +2,6 @@ namespace Microsoft.EntityFrameworkCore.Query; public class OwnedEntityQueryNpgsqlTest(NonSharedFixture fixture) : OwnedEntityQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs index 11c5da52f..663c6c1ee 100644 --- a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs @@ -1,3 +1,5 @@ +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + namespace Microsoft.EntityFrameworkCore.Query; public class PrimitiveCollectionsQueryNpgsqlTest : PrimitiveCollectionsQueryRelationalTestBase< @@ -2598,6 +2600,121 @@ WHERE NOT (p."Int" = ANY (@ints) AND p."Int" = ANY (@ints) IS NOT NULL) """); } + [ConditionalFact] + public override async Task Multidimensional_array_is_not_supported() + { + // Multidimensional arrays are supported in PostgreSQL (via the regular array type); the EFCore.PG maps .NET + // multidimensional arrays. However, arrays of multidimensional arrays aren't supported (since arrays of arrays generally aren't + // supported). + var contextFactory = await InitializeNonSharedTest( + mb => mb.Entity().Property("MultidimensionalArray"), + seed: async context => + { + var entry = context.Add(new TestEntity()); + entry.Property("MultidimensionalArray").CurrentValue = new[,] { { 1, 2 }, { 3, 4 } }; + await context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateDbContext(); + + var arrays = new[] { new[,] { { 1, 2 }, { 3, 4 } }, new[,] { { 1, 2 }, { 3, 5 } } }; + + await Assert.ThrowsAsync( + () => + context.Set().Where(t => arrays.Contains(EF.Property(t, "MultidimensionalArray"))).ToArrayAsync()); + } + + public override async Task Column_collection_inside_json_owned_entity() + { + await base.Column_collection_inside_json_owned_entity(); + + AssertSql( + """ +SELECT t."Id", t."Owned" +FROM "TestOwner" AS t +WHERE jsonb_array_length(t."Owned" -> 'Strings') = 2 +LIMIT 2 +""", + // + """ +SELECT t."Id", t."Owned" +FROM "TestOwner" AS t +WHERE (t."Owned" #>> '{Strings,1}') = 'bar' +LIMIT 2 +"""); + } + + #region Contains with various index methods + + // For Contains over column collections that have a (modeled) GIN index, we translate to the containment operator (@>). + // Otherwise we translate to the ANY construct. + [ConditionalFact] + public virtual async Task Column_collection_Contains_with_GIN_index_uses_containment() + { + var contextFactory = await InitializeNonSharedTest( + onModelCreating: mb => mb.Entity() + .HasIndex(e => e.Ints) + .HasMethod("GIN"), + seed: context => + { + context.AddRange( + new TestEntity { Id = 1, Ints = [1, 2, 3] }, + new TestEntity { Id = 2, Ints = [1, 2, 4] }); + return context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateDbContext(); + + var result = await context.Set().Where(c => c.Ints!.Contains(4)).SingleAsync(); + Assert.Equal(2, result.Id); + + AssertSql( + """ +SELECT t."Id", t."Ints" +FROM "TestEntity" AS t +WHERE t."Ints" @> ARRAY[4]::integer[] +LIMIT 2 +"""); + } + + [ConditionalFact] + public virtual async Task Column_collection_Contains_with_btree_index_does_not_use_containment() + { + var contextFactory = await InitializeNonSharedTest( + onModelCreating: mb => mb.Entity().HasIndex(e => e.Ints), + seed: context => + { + context.AddRange( + new TestEntity { Id = 1, Ints = [1, 2, 3] }, + new TestEntity { Id = 2, Ints = [1, 2, 4] }); + return context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateDbContext(); + + var result = await context.Set().Where(c => c.Ints!.Contains(4)).SingleAsync(); + Assert.Equal(2, result.Id); + + AssertSql( + """ +SELECT t."Id", t."Ints" +FROM "TestEntity" AS t +WHERE 4 = ANY (t."Ints") +LIMIT 2 +"""); + } + + #endregion Contains with various index methods + + protected override DbContextOptionsBuilder SetParameterizedCollectionMode( + DbContextOptionsBuilder optionsBuilder, + ParameterTranslationMode parameterizedCollectionMode) + { + new NpgsqlDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); + + return optionsBuilder; + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.PG.FunctionalTests/Query/SharedTypeQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/SharedTypeQueryNpgsqlTest.cs index f217715c7..067f1bd91 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SharedTypeQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SharedTypeQueryNpgsqlTest.cs @@ -2,7 +2,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class SharedTypeQueryNpgsqlTest(NonSharedFixture fixture) : SharedTypeQueryRelationalTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; public override Task Can_use_shared_type_entity_type_in_query_filter_with_from_sql(bool async) diff --git a/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs index 96ea46d87..cd2438746 100644 --- a/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs @@ -2,7 +2,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class ToSqlQuerySqlServerTest(NonSharedFixture fixture) : ToSqlQueryTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; [ConditionalFact] diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/BigIntegerTranslationsTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/BigIntegerTranslationsTest.cs index 23f3f8aaf..e3e9899ff 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/BigIntegerTranslationsTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/BigIntegerTranslationsTest.cs @@ -144,7 +144,7 @@ public class Entity public BigInteger BigInteger { get; set; } } - public class BigIntegerQueryFixture : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory + public class BigIntegerQueryFixture : QueryFixtureBase, ITestSqlLoggerFactory { private BigIntegerData? _expectedData; @@ -160,17 +160,14 @@ public TestSqlLoggerFactory TestSqlLoggerFactory protected override Task SeedAsync(BigIntegerQueryContext context) => BigIntegerQueryContext.SeedAsync(context); - public Func GetContextCreator() - => CreateContext; - - public ISetSource GetExpectedData() + public override ISetSource GetExpectedData() => _expectedData ??= new BigIntegerData(); - public IReadOnlyDictionary EntitySorters + public override IReadOnlyDictionary EntitySorters => new Dictionary> { { typeof(Entity), e => ((Entity)e).Id } } .ToDictionary(e => e.Key, e => (object)e.Value); - public IReadOnlyDictionary EntityAsserters + public override IReadOnlyDictionary EntityAsserters => new Dictionary> { { diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/EnumTranslationsTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/EnumTranslationsTest.cs index dc1432593..ded37ae7e 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/EnumTranslationsTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/EnumTranslationsTest.cs @@ -310,7 +310,7 @@ public enum UnmappedByteEnum : byte } // ReSharper disable once ClassNeverInstantiated.Global - public class EnumFixture : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory + public class EnumFixture : QueryFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "EnumQueryTest"; @@ -342,17 +342,14 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build protected override Task SeedAsync(EnumContext context) => EnumContext.SeedAsync(context); - public Func GetContextCreator() - => CreateContext; - - public ISetSource GetExpectedData() + public override ISetSource GetExpectedData() => _expectedData ??= new EnumData(); - public IReadOnlyDictionary EntitySorters + public override IReadOnlyDictionary EntitySorters => new Dictionary> { { typeof(SomeEnumEntity), e => ((SomeEnumEntity)e)?.Id } } .ToDictionary(e => e.Key, e => (object)e.Value); - public IReadOnlyDictionary EntityAsserters + public override IReadOnlyDictionary EntityAsserters => new Dictionary> { { diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/JsonTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/JsonTranslationsNpgsqlTest.cs index 9e0586970..8d96701eb 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/JsonTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/JsonTranslationsNpgsqlTest.cs @@ -10,26 +10,26 @@ public JsonTranslationsNpgsqlTest(JsonTranslationsQueryNpgsqlFixture fixture, IT } - public override async Task JsonExists_on_scalar_string_column() + public override async Task JsonPathExists_on_scalar_string_column() { // TODO: #3733 - await AssertTranslationFailed(base.JsonExists_on_scalar_string_column); + await AssertTranslationFailed(base.JsonPathExists_on_scalar_string_column); AssertSql(); } - public override async Task JsonExists_on_complex_property() + public override async Task JsonPathExists_on_complex_property() { // TODO: #3733 - await AssertTranslationFailed(base.JsonExists_on_complex_property); + await AssertTranslationFailed(base.JsonPathExists_on_complex_property); AssertSql(); } - public override async Task JsonExists_on_owned_entity() + public override async Task JsonPathExists_on_owned_entity() { // TODO: #3733 - await AssertTranslationFailed(base.JsonExists_on_owned_entity); + await AssertTranslationFailed(base.JsonPathExists_on_owned_entity); AssertSql(); } @@ -46,21 +46,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().Property(e => e.JsonString).HasColumnType("jsonb"); } - protected override string RemoveJsonProperty(string column, string jsonPath) - { - // HACK. PostgreSQL doesn't have a delete function accepting JSON path, but the base class requires this - // only for a single path segment, which we can do. Rethink this mechanism in EF. - if (jsonPath.StartsWith("$.")) - { - var segment = jsonPath[2..]; - if (!segment.Contains('.')) - { - return $"{column} - '{segment}'"; - } - } - - throw new UnreachableException(); - } + protected override string RemoveJsonProperty(string column, string property) + => $"{column} - '{property}'"; } [ConditionalFact] diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/NodaTime/NodaTimeQueryNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/NodaTime/NodaTimeQueryNpgsqlFixture.cs index 290311454..18753fe18 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/NodaTime/NodaTimeQueryNpgsqlFixture.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/NodaTime/NodaTimeQueryNpgsqlFixture.cs @@ -4,7 +4,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Translations.NodaTime; -public class NodaTimeQueryNpgsqlFixture : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory +public class NodaTimeQueryNpgsqlFixture : QueryFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "NodaTimeQueryTest"; @@ -36,17 +36,14 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build protected override Task SeedAsync(NodaTimeContext context) => NodaTimeContext.SeedAsync(context); - public Func GetContextCreator() - => CreateContext; - - public ISetSource GetExpectedData() + public override ISetSource GetExpectedData() => _expectedData ??= new NodaTimeData(); - public IReadOnlyDictionary EntitySorters + public override IReadOnlyDictionary EntitySorters => new Dictionary> { { typeof(NodaTimeTypes), e => ((NodaTimeTypes)e).Id } } .ToDictionary(e => e.Key, e => (object)e.Value); - public IReadOnlyDictionary EntityAsserters + public override IReadOnlyDictionary EntityAsserters => new Dictionary> { { diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/TimestampTranslationsTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/TimestampTranslationsTest.cs index e67bf09db..5dada1dd3 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/TimestampTranslationsTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/TimestampTranslationsTest.cs @@ -909,7 +909,7 @@ public class Entity public NpgsqlRange TimestampDateTimeRange { get; set; } } - public class TimestampQueryFixture : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory + public class TimestampQueryFixture : QueryFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "TimestampQueryTest"; @@ -928,17 +928,14 @@ public TestSqlLoggerFactory TestSqlLoggerFactory protected override Task SeedAsync(TimestampQueryContext context) => TimestampQueryContext.SeedAsync(context); - public Func GetContextCreator() - => CreateContext; - - public ISetSource GetExpectedData() + public override ISetSource GetExpectedData() => _expectedData ??= new TimestampData(); - public IReadOnlyDictionary EntitySorters + public override IReadOnlyDictionary EntitySorters => new Dictionary> { { typeof(Entity), e => ((Entity)e)?.Id } } .ToDictionary(e => e.Key, e => (object)e.Value); - public IReadOnlyDictionary EntityAsserters + public override IReadOnlyDictionary EntityAsserters => new Dictionary> { { diff --git a/test/EFCore.PG.FunctionalTests/TPTTableSplittingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/TPTTableSplittingNpgsqlTest.cs index 7c35b9b24..3c2c1c649 100644 --- a/test/EFCore.PG.FunctionalTests/TPTTableSplittingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/TPTTableSplittingNpgsqlTest.cs @@ -7,6 +7,6 @@ public override Task Can_insert_dependent_with_just_one_parent() // This scenario is not valid for TPT => Task.CompletedTask; - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs index ca7b8f12c..6e626746e 100644 --- a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs @@ -6,7 +6,7 @@ namespace Microsoft.EntityFrameworkCore; public class TableSplittingNpgsqlTest(NonSharedFixture fixture, ITestOutputHelper testOutputHelper) : TableSplittingTestBase(fixture, testOutputHelper) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; public override async Task ExecuteUpdate_works_for_table_sharing(bool async) diff --git a/test/EFCore.PG.FunctionalTests/Update/NonSharedModelUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Update/NonSharedModelUpdatesNpgsqlTest.cs index 9d4a5206b..6cc010337 100644 --- a/test/EFCore.PG.FunctionalTests/Update/NonSharedModelUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Update/NonSharedModelUpdatesNpgsqlTest.cs @@ -2,6 +2,6 @@ namespace Microsoft.EntityFrameworkCore.Update; public class NonSharedModelUpdatesNpgsqlTest(NonSharedFixture fixture) : NonSharedModelUpdatesTestBase(fixture) { - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Update/StoredProcedureUpdateNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Update/StoredProcedureUpdateNpgsqlTest.cs index 065590461..90bbf6001 100644 --- a/test/EFCore.PG.FunctionalTests/Update/StoredProcedureUpdateNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Update/StoredProcedureUpdateNpgsqlTest.cs @@ -238,7 +238,7 @@ public virtual async Task Rows_affected_parameter_with_another_output_parameter( END $$ """; - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeNonSharedTest( modelBuilder => modelBuilder.Entity() .UpdateUsingStoredProcedure( nameof(EntityWithAdditionalProperty) + "_Update", @@ -250,7 +250,7 @@ public virtual async Task Rows_affected_parameter_with_another_output_parameter( .Property(w => w.AdditionalProperty).HasComputedColumnSql("8", stored: true), seed: ctx => CreateStoredProcedures(ctx, createSprocSql)); - await using var context = contextFactory.CreateContext(); + await using var context = contextFactory.CreateDbContext(); var entity = new EntityWithAdditionalProperty { Name = "Initial" }; context.Set().Add(entity); @@ -560,6 +560,6 @@ protected override void ConfigureStoreGeneratedConcurrencyToken(EntityTypeBuilde .ValueGeneratedOnAddOrUpdate() .IsConcurrencyToken(); - protected override ITestStoreFactory TestStoreFactory + protected override ITestStoreFactory NonSharedTestStoreFactory => NpgsqlTestStoreFactory.Instance; }