diff --git a/Directory.Build.props b/Directory.Build.props index 5b52114b7..6141e141e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 11.0.0-rc.3 + 11.0.0 net11.0 latest enable diff --git a/Directory.Packages.props b/Directory.Packages.props index 7b33205a2..aa3684915 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,8 +1,8 @@ - 11.0.0-rc.3.26522.148 - 11.0.0-rc.3.26522.148 - 11.0.0-rc.3.26522.148 + 11.0.0 + 11.0.0 + 11.0.0 10.0.3 4.0.1 @@ -12,11 +12,11 @@ Dependencies on EF preview versions should be locked to a specific version (as provider-facing breaking changes are frequent). For released versions, depend on anything in the current major version to allow users to update to higher patch versions. --> - - - - - + + + + + diff --git a/global.json b/global.json index dad3b97cb..fe5bfbb1d 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,6 @@ { "sdk": { - "version": "11.0.100-rc.3.26522.148", - "allowPrerelease": true + "version": "11.0.100" }, "test": { "runner": "Microsoft.Testing.Platform" diff --git a/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationProvider.cs b/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationProvider.cs index 9956b3c95..b615c5303 100644 --- a/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationProvider.cs +++ b/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationProvider.cs @@ -150,14 +150,10 @@ public override IEnumerable For(ITableIndex index, bool designTime) foreach (var annotation in base.For(index, designTime)) { - // TODO: Remove the whole-document exclusion after consuming https://github.com/dotnet/efcore/pull/39089. - // EF 11 RC2 cannot scaffold Relational:JsonIndex; excluding it preserves existing whole-document scaffolding. - if (annotation.Value is RelationalJsonIndex jsonIndex - && (jsonIndex.Elements.Any(e => e.Path.Count > 0) - || jsonIndex.CollectionIndices?.Any(indices => indices?.Any(i => i.HasValue) == true) == true)) - { - yield return annotation; + yield return annotation; + if (annotation.Value is RelationalJsonIndex jsonIndex) + { // JSON paths alone don't capture changes to the casts used by PostgreSQL expression indexes. yield return new Annotation( NpgsqlAnnotationNames.JsonIndexTypes, diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index e1a5dcb2a..d1bc0cfb8 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -2507,7 +2507,7 @@ private static bool TryParseSchema(string identifier, out string name, out strin /// /// Builds index keys using facets from . When supplied, - /// defines the keys and extraction expressions instead of the operation's deduplicated container columns. + /// defines the keys, extraction expressions and sort directions instead of the operation's deduplicated container columns. /// private IndexColumn[] GetIndexColumns(CreateIndexOperation operation, RelationalJsonIndex? jsonIndex) { @@ -2528,9 +2528,11 @@ private IndexColumn[] GetIndexColumns(CreateIndexOperation operation, Relational var name = jsonIndex?.Elements[i].ContainingColumn.Name ?? operation.Columns[i]; var @operator = i < operators?.Length ? operators[i] : null; var collation = i < collations?.Length ? collations[i] : null; - var isColumnDescending = isDescendingValues is not null - ? isDescendingValues.Length == 0 || isDescendingValues[i] - : i < legacySortOrders?.Length && legacySortOrders[i] == SortOrder.Descending; + var isColumnDescending = jsonIndex is not null + ? jsonIndex.IsElementDescending(i) + : isDescendingValues is not null + ? isDescendingValues.Length == 0 || isDescendingValues[i] + : i < legacySortOrders?.Length && legacySortOrders[i] == SortOrder.Descending; var nullSortOrder = i < nullSortOrders?.Length ? nullSortOrders[i] : NullSortOrder.Unspecified; columns[i] = new IndexColumn( diff --git a/src/EFCore.PG/Query/Internal/NpgsqlSqlTranslatingExpressionVisitor.cs b/src/EFCore.PG/Query/Internal/NpgsqlSqlTranslatingExpressionVisitor.cs index 18c6cc220..70dc2494a 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlSqlTranslatingExpressionVisitor.cs @@ -47,65 +47,6 @@ protected override Expression VisitExtension(Expression extensionExpression) ? array : base.VisitExtension(extensionExpression); - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - protected override Expression VisitConditional(ConditionalExpression conditionalExpression) - { - var test = Visit(conditionalExpression.Test); - var ifTrue = Visit(conditionalExpression.IfTrue); - var ifFalse = Visit(conditionalExpression.IfFalse); - - if (TranslationFailed(conditionalExpression.Test, test, out var sqlTest) - || TranslationFailed(conditionalExpression.IfTrue, ifTrue, out var sqlIfTrue) - || TranslationFailed(conditionalExpression.IfFalse, ifFalse, out var sqlIfFalse)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - // Translate: - // a == b ? null : a -> NULLIF(a, b) - // a != b ? a : null -> NULLIF(a, b) - if (sqlTest is SqlBinaryExpression binary && sqlIfTrue is not null && sqlIfFalse is not null) - { - switch (binary.OperatorType) - { - case ExpressionType.Equal - when ifTrue is SqlConstantExpression { Value: null } && TryTranslateToNullIf(sqlIfFalse, out var nullIfTranslation): - case ExpressionType.NotEqual - when ifFalse is SqlConstantExpression { Value: null } && TryTranslateToNullIf(sqlIfTrue, out nullIfTranslation): - return nullIfTranslation; - } - } - - return _sqlExpressionFactory.Case([new CaseWhenClause(sqlTest!, sqlIfTrue!)], sqlIfFalse); - - bool TryTranslateToNullIf(SqlExpression conditionalResult, [NotNullWhen(true)] out Expression? nullIfTranslation) - { - var (left, right) = (binary.Left, binary.Right); - - if (left.Equals(conditionalResult)) - { - nullIfTranslation = _sqlExpressionFactory.Function( - "NULLIF", [left, right], true, [false, false], left.Type, left.TypeMapping); - return true; - } - - if (right.Equals(conditionalResult)) - { - nullIfTranslation = _sqlExpressionFactory.Function( - "NULLIF", [right, left], true, [false, false], right.Type, right.TypeMapping); - return true; - } - - nullIfTranslation = null; - return false; - } - } - /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs index 8087753ff..4f2f10d8a 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs @@ -895,8 +895,7 @@ public void Create_json_member_composite_index(bool descending) { Name = "IX_Json", Table = "Blogs", - Columns = ["Details"], - IsDescending = descending ? [] : null + Columns = ["Details"] }); var direction = descending ? " DESC" : ""; @@ -911,7 +910,7 @@ public void Create_json_member_index_with_options() b => { BuildJsonIndexModel(b); - b.Entity().HasIndex(e => e.Details.Slug).HasDatabaseName("IX_Json"); + b.Entity().HasIndex(e => e.Details.Slug).HasDatabaseName("IX_Json").IsDescending(); }, new CreateIndexOperation { @@ -919,7 +918,6 @@ public void Create_json_member_index_with_options() Table = "Blogs", Columns = ["Details"], IsUnique = true, - IsDescending = [true], Filter = "\"Id\" > 0", [NpgsqlAnnotationNames.CreatedConcurrently] = true, [NpgsqlAnnotationNames.IndexOperators] = new[] { "text_ops" }, diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousNpgsqlTest.cs index 8f67b0179..8105675aa 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousNpgsqlTest.cs @@ -86,12 +86,31 @@ public override async Task Where_HasValue_on_nullable_value_type() #endregion Value types - // https://github.com/dotnet/efcore/issues/34627 public override async Task FromSql_on_root() { - await Assert.ThrowsAnyAsync(base.FromSql_on_root); + await base.FromSql_on_root(); - AssertSql(); + AssertSql( + """ +SELECT m."Id", m."Name", m."AssociateCollection", m."OptionalAssociate", m."RequiredAssociate" +FROM ( + SELECT * FROM "RootEntity" +) AS m +"""); + } + + public override async Task FromSql_on_root_composed(bool async) + { + await base.FromSql_on_root_composed(async); + + AssertSql( + """ +SELECT m."Id", m."Name", m."AssociateCollection", m."OptionalAssociate", m."RequiredAssociate" +FROM ( + SELECT * FROM "RootEntity" +) AS m +WHERE (CAST(m."RequiredAssociate" ->> 'Int' AS integer)) = 8 +"""); } [ConditionalFact] diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsBulkUpdateNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsBulkUpdateNpgsqlTest.cs index 771237585..0d70705df 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsBulkUpdateNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsBulkUpdateNpgsqlTest.cs @@ -22,27 +22,100 @@ public override Task Delete_required_associate() public override Task Delete_optional_associate() => Assert.ThrowsAsync(base.Delete_optional_associate); - // PostgreSQL generates valid SQL for these but produces wrong row counts due to self-joins in UPDATE ... FROM - public override Task Update_property_inside_associate() - => Assert.ThrowsAsync(base.Update_property_inside_associate); + public override async Task Update_property_inside_associate() + { + await base.Update_property_inside_associate(); + + AssertExecuteUpdateSql( + """ +@p='foo_updated' + +UPDATE "AssociateType" AS a +SET "String" = @p +FROM "RootEntity" AS r +WHERE r."RequiredAssociateId" = a."Id" +"""); + } + + public override async Task Update_property_inside_associate_with_special_chars() + { + await base.Update_property_inside_associate_with_special_chars(); + + AssertExecuteUpdateSql( + """ +UPDATE "AssociateType" AS a0 +SET "String" = '{ Some other/JSON:like text though it [isn''t]: ממש ממש לאéèéè }' +FROM "RootEntity" AS r +INNER JOIN "AssociateType" AS a ON r."RequiredAssociateId" = a."Id" +WHERE r."RequiredAssociateId" = a0."Id" AND a."String" = '{ this may/look:like JSON but it [isn''t]: ממש ממש לאéèéè }' +"""); + } - public override Task Update_property_inside_associate_with_special_chars() - => Assert.ThrowsAsync(base.Update_property_inside_associate_with_special_chars); + public override async Task Update_property_on_projected_associate() + { + await base.Update_property_on_projected_associate(); - public override Task Update_property_on_projected_associate() - => Assert.ThrowsAsync(base.Update_property_on_projected_associate); + AssertExecuteUpdateSql( + """ +@p='foo_updated' + +UPDATE "AssociateType" AS a +SET "String" = @p +FROM "RootEntity" AS r +WHERE r."RequiredAssociateId" = a."Id" +"""); + } + // PostgreSQL produces wrong row counts due to self-joins in UPDATE ... FROM. public override Task Update_property_on_projected_associate_with_OrderBy_Skip() => Assert.ThrowsAsync(base.Update_property_on_projected_associate_with_OrderBy_Skip); - public override Task Update_multiple_properties_inside_same_associate() - => Assert.ThrowsAsync(base.Update_multiple_properties_inside_same_associate); + public override async Task Update_multiple_properties_inside_same_associate() + { + await base.Update_multiple_properties_inside_same_associate(); + + AssertExecuteUpdateSql( + """ +@p='foo_updated' +@p1='20' + +UPDATE "AssociateType" AS a +SET "String" = @p, + "Int" = @p1 +FROM "RootEntity" AS r +WHERE r."RequiredAssociateId" = a."Id" +"""); + } + + public override async Task Update_primitive_collection_to_constant() + { + await base.Update_primitive_collection_to_constant(); + + AssertExecuteUpdateSql( + """ +UPDATE "AssociateType" AS a +SET "Ints" = ARRAY[1,2,4]::integer[] +FROM "RootEntity" AS r +WHERE r."RequiredAssociateId" = a."Id" +"""); + } - public override Task Update_primitive_collection_to_constant() - => Assert.ThrowsAsync(base.Update_primitive_collection_to_constant); + public override async Task Update_primitive_collection_to_parameter() + { + await base.Update_primitive_collection_to_parameter(); - public override Task Update_primitive_collection_to_parameter() - => Assert.ThrowsAsync(base.Update_primitive_collection_to_parameter); + AssertExecuteUpdateSql( + """ +@ints={ '1' +'2' +'4' } (DbType = Object) + +UPDATE "AssociateType" AS a +SET "Ints" = @ints +FROM "RootEntity" AS r +WHERE r."RequiredAssociateId" = a."Id" +"""); + } // Translation not yet supported for navigation-mapped associations public override Task Update_associate_to_parameter() diff --git a/test/EFCore.PG.FunctionalTests/Query/ComplexTypeQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/ComplexTypeQueryNpgsqlTest.cs index 59eadd36c..1a4e35c11 100644 --- a/test/EFCore.PG.FunctionalTests/Query/ComplexTypeQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/ComplexTypeQueryNpgsqlTest.cs @@ -1394,10 +1394,10 @@ public override async Task Select_TPC_base_with_ComplexType() AssertSql( """ -SELECT t."Id", t."ChildProperty", NULL AS "ChildProperty1", t."PropertyInsideComplexThing", t."ChildComplexProperty_PropertyInsideComplexThing", NULL AS "ChildComplexProperty_PropertyInsideComplexThing1", 'TpcChild1' AS "Discriminator" +SELECT t."Id", t."ChildProperty", NULL AS "ChildProperty1", t."BaseComplexProperty_PropertyInsideComplexThing", t."ChildComplexProperty_PropertyInsideComplexThing", NULL AS "ChildComplexProperty_PropertyInsideComplexThing1", 'TpcChild1' AS "Discriminator" FROM "TpcChild1" AS t UNION ALL -SELECT t0."Id", NULL AS "ChildProperty", t0."ChildProperty" AS "ChildProperty1", t0."PropertyInsideComplexThing", NULL AS "ChildComplexProperty_PropertyInsideComplexThing", t0."ChildComplexProperty_PropertyInsideComplexThing" AS "ChildComplexProperty_PropertyInsideComplexThing1", 'TpcChild2' AS "Discriminator" +SELECT t0."Id", NULL AS "ChildProperty", t0."ChildProperty" AS "ChildProperty1", t0."BaseComplexProperty_PropertyInsideComplexThing", NULL AS "ChildComplexProperty_PropertyInsideComplexThing", t0."ChildComplexProperty_PropertyInsideComplexThing" AS "ChildComplexProperty_PropertyInsideComplexThing1", 'TpcChild2' AS "Discriminator" FROM "TpcChild2" AS t0 """); } diff --git a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs index 7408602e8..bab6bb497 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs @@ -217,13 +217,13 @@ public override async Task Can_query_all_types_when_shared_column(bool async) SELECT d."Id", d."SortIndex", d."ComplexTypeCollection", d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL::int AS "LiltCO2", NULL::int AS "SugarGrams1", NULL::int AS "CaffeineGrams1", NULL::boolean AS "HasMilk", NULL::int AS "ChildComplexType_Int1", NULL::int AS "ChildComplexType_UniqueInt1", NULL::int AS "ChildComplexType_Nested_NestedInt1", NULL::int AS "ChildComplexType_Nested_UniqueInt1", 'Drink' AS "Discriminator" FROM "Drinks" AS d UNION ALL - SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" + SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" FROM "Coke" AS c UNION ALL - SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" + SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" FROM "Lilt" AS l UNION ALL - SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" + SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" FROM "Tea" AS t ) AS u """); @@ -259,19 +259,19 @@ public override async Task Can_query_when_shared_column(bool async) AssertSql( """ -SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."Int", c."UniqueInt", c."NestedInt", c."NestedComplexType_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" +SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" FROM "Coke" AS c LIMIT 2 """, // """ -SELECT l."Id", l."SortIndex", l."LiltCO2", l."SugarGrams", l."ComplexTypeCollection", l."Int", l."UniqueInt", l."NestedInt", l."NestedComplexType_UniqueInt" +SELECT l."Id", l."SortIndex", l."LiltCO2", l."SugarGrams", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt" FROM "Lilt" AS l LIMIT 2 """, // """ -SELECT t."Id", t."SortIndex", t."CaffeineGrams", t."HasMilk", t."ComplexTypeCollection", t."Int", t."UniqueInt", t."NestedInt", t."NestedComplexType_UniqueInt", t."ChildComplexType_Int", t."ChildComplexType_UniqueInt", t."ChildComplexType_Nested_NestedInt", t."ChildComplexType_Nested_UniqueInt" +SELECT t."Id", t."SortIndex", t."CaffeineGrams", t."HasMilk", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", t."ChildComplexType_Int", t."ChildComplexType_UniqueInt", t."ChildComplexType_Nested_NestedInt", t."ChildComplexType_Nested_UniqueInt" FROM "Tea" AS t LIMIT 2 """); @@ -811,13 +811,13 @@ public override async Task Primitive_collection_on_subtype(bool async) SELECT d."Id", d."SortIndex", d."ComplexTypeCollection", d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL::int AS "LiltCO2", NULL::int AS "SugarGrams1", NULL::int AS "CaffeineGrams1", NULL::boolean AS "HasMilk", NULL::int AS "ChildComplexType_Int1", NULL::int AS "ChildComplexType_UniqueInt1", NULL::int AS "ChildComplexType_Nested_NestedInt1", NULL::int AS "ChildComplexType_Nested_UniqueInt1", 'Drink' AS "Discriminator" FROM "Drinks" AS d UNION ALL - SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" + SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" FROM "Coke" AS c UNION ALL - SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" + SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" FROM "Lilt" AS l UNION ALL - SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" + SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" FROM "Tea" AS t ) AS u WHERE cardinality(u."Ints") > 0 diff --git a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceTableSplittingQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceTableSplittingQueryNpgsqlTest.cs index 2f2654dd9..975b5cb2d 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceTableSplittingQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceTableSplittingQueryNpgsqlTest.cs @@ -12,7 +12,7 @@ public override async Task Filter_on_complex_type_property_on_derived_type(bool AssertSql( """ -SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."Int", c."UniqueInt", c."NestedInt", c."NestedComplexType_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" +SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" FROM "Coke" AS c WHERE c."ChildComplexType_Int" = 10 """); @@ -29,13 +29,13 @@ public override async Task Filter_on_complex_type_property_on_base_type(bool asy SELECT d."Id", d."SortIndex", d."ComplexTypeCollection", d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL::int AS "LiltCO2", NULL::int AS "SugarGrams1", NULL::int AS "CaffeineGrams1", NULL::boolean AS "HasMilk", NULL::int AS "ChildComplexType_Int1", NULL::int AS "ChildComplexType_UniqueInt1", NULL::int AS "ChildComplexType_Nested_NestedInt1", NULL::int AS "ChildComplexType_Nested_UniqueInt1", 'Drink' AS "Discriminator" FROM "Drinks" AS d UNION ALL - SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" + SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" FROM "Coke" AS c UNION ALL - SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" + SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" FROM "Lilt" AS l UNION ALL - SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" + SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" FROM "Tea" AS t ) AS u WHERE u."ParentComplexType_Int" = 8 @@ -48,7 +48,7 @@ public override async Task Filter_on_nested_complex_type_property_on_derived_typ AssertSql( """ -SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."Int", c."UniqueInt", c."NestedInt", c."NestedComplexType_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" +SELECT c."Id", c."SortIndex", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt" FROM "Coke" AS c WHERE c."ChildComplexType_Nested_NestedInt" = 58 """); @@ -65,13 +65,13 @@ public override async Task Filter_on_nested_complex_type_property_on_base_type(b SELECT d."Id", d."SortIndex", d."ComplexTypeCollection", d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL::int AS "LiltCO2", NULL::int AS "SugarGrams1", NULL::int AS "CaffeineGrams1", NULL::boolean AS "HasMilk", NULL::int AS "ChildComplexType_Int1", NULL::int AS "ChildComplexType_UniqueInt1", NULL::int AS "ChildComplexType_Nested_NestedInt1", NULL::int AS "ChildComplexType_Nested_UniqueInt1", 'Drink' AS "Discriminator" FROM "Drinks" AS d UNION ALL - SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" + SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" FROM "Coke" AS c UNION ALL - SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" + SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" FROM "Lilt" AS l UNION ALL - SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" + SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" FROM "Tea" AS t ) AS u WHERE u."ParentComplexType_Nested_NestedInt" = 50 @@ -98,13 +98,13 @@ public override async Task Project_complex_type_on_base_type(bool async) SELECT d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt" FROM "Drinks" AS d UNION ALL -SELECT c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt" FROM "Coke" AS c UNION ALL -SELECT l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt" FROM "Lilt" AS l UNION ALL -SELECT t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt" FROM "Tea" AS t """); } @@ -129,13 +129,13 @@ public override async Task Project_nested_complex_type_on_base_type(bool async) SELECT d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt" FROM "Drinks" AS d UNION ALL -SELECT c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt" FROM "Coke" AS c UNION ALL -SELECT l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt" FROM "Lilt" AS l UNION ALL -SELECT t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt" +SELECT t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt" FROM "Tea" AS t """); } @@ -151,13 +151,13 @@ public override async Task Subquery_over_complex_collection(bool async) SELECT d."Id", d."SortIndex", d."ComplexTypeCollection", d."ParentComplexType_Int", d."ParentComplexType_UniqueInt", d."ParentComplexType_Nested_NestedInt", d."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL::int AS "LiltCO2", NULL::int AS "SugarGrams1", NULL::int AS "CaffeineGrams1", NULL::boolean AS "HasMilk", NULL::int AS "ChildComplexType_Int1", NULL::int AS "ChildComplexType_UniqueInt1", NULL::int AS "ChildComplexType_Nested_NestedInt1", NULL::int AS "ChildComplexType_Nested_UniqueInt1", 'Drink' AS "Discriminator" FROM "Drinks" AS d UNION ALL - SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."Int" AS "ParentComplexType_Int", c."UniqueInt" AS "ParentComplexType_UniqueInt", c."NestedInt" AS "ParentComplexType_Nested_NestedInt", c."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" + SELECT c."Id", c."SortIndex", c."ComplexTypeCollection", c."ParentComplexType_Int", c."ParentComplexType_UniqueInt", c."ParentComplexType_Nested_NestedInt", c."ParentComplexType_Nested_UniqueInt", c."CaffeineGrams", c."CokeCO2", c."Ints", c."SugarGrams", c."ChildComplexType_Int", c."ChildComplexType_UniqueInt", c."ChildComplexType_Nested_NestedInt", c."ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Coke' AS "Discriminator" FROM "Coke" AS c UNION ALL - SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."Int" AS "ParentComplexType_Int", l."UniqueInt" AS "ParentComplexType_UniqueInt", l."NestedInt" AS "ParentComplexType_Nested_NestedInt", l."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" + SELECT l."Id", l."SortIndex", l."ComplexTypeCollection", l."ParentComplexType_Int", l."ParentComplexType_UniqueInt", l."ParentComplexType_Nested_NestedInt", l."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", l."LiltCO2", l."SugarGrams" AS "SugarGrams1", NULL AS "CaffeineGrams1", NULL AS "HasMilk", NULL AS "ChildComplexType_Int1", NULL AS "ChildComplexType_UniqueInt1", NULL AS "ChildComplexType_Nested_NestedInt1", NULL AS "ChildComplexType_Nested_UniqueInt1", 'Lilt' AS "Discriminator" FROM "Lilt" AS l UNION ALL - SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."Int" AS "ParentComplexType_Int", t."UniqueInt" AS "ParentComplexType_UniqueInt", t."NestedInt" AS "ParentComplexType_Nested_NestedInt", t."NestedComplexType_UniqueInt" AS "ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" + SELECT t."Id", t."SortIndex", t."ComplexTypeCollection", t."ParentComplexType_Int", t."ParentComplexType_UniqueInt", t."ParentComplexType_Nested_NestedInt", t."ParentComplexType_Nested_UniqueInt", NULL AS "CaffeineGrams", NULL AS "CokeCO2", NULL AS "Ints", NULL AS "SugarGrams", NULL AS "ChildComplexType_Int", NULL AS "ChildComplexType_UniqueInt", NULL AS "ChildComplexType_Nested_NestedInt", NULL AS "ChildComplexType_Nested_UniqueInt", NULL AS "LiltCO2", NULL AS "SugarGrams1", t."CaffeineGrams" AS "CaffeineGrams1", t."HasMilk", t."ChildComplexType_Int" AS "ChildComplexType_Int1", t."ChildComplexType_UniqueInt" AS "ChildComplexType_UniqueInt1", t."ChildComplexType_Nested_NestedInt" AS "ChildComplexType_Nested_NestedInt1", t."ChildComplexType_Nested_UniqueInt" AS "ChildComplexType_Nested_UniqueInt1", 'Tea' AS "Discriminator" FROM "Tea" AS t ) AS u WHERE ( diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs index 9da47f45a..ca2aefba5 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs @@ -18,6 +18,39 @@ public NorthwindGroupByQueryNpgsqlTest( public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); + public override async Task SelectMany_DefaultIfEmpty_over_GroupBy(bool async) + { + await base.SelectMany_DefaultIfEmpty_over_GroupBy(async); + + AssertSql( + """ +SELECT p."ProductID", o0."Key", COALESCE(o0."Sum", 0) AS "Sum" +FROM "Products" AS p +LEFT JOIN ( + SELECT o."ProductID" AS "Key", COALESCE(sum(o."Quantity"::int), 0)::int AS "Sum", o."ProductID" + FROM "Order Details" AS o + WHERE o."ProductID" < 10 + GROUP BY o."ProductID" +) AS o0 ON p."ProductID" = o0."ProductID" +"""); + } + + public override async Task SelectMany_DefaultIfEmpty_over_Distinct(bool async) + { + await base.SelectMany_DefaultIfEmpty_over_Distinct(async); + + AssertSql( + """ +SELECT p."ProductID", o0."Key", COALESCE(o0."Quantity", 0) AS "Quantity" +FROM "Products" AS p +LEFT JOIN ( + SELECT DISTINCT o."ProductID" AS "Key", o."Quantity", 1 AS marker, o."ProductID" + FROM "Order Details" AS o + WHERE o."ProductID" < 10 +) AS o0 ON p."ProductID" = o0."ProductID" +"""); + } + #region GroupByProperty public override async Task GroupBy_Property_Select_Average(bool async) diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs index 442491647..5010e8a90 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs @@ -108,6 +108,42 @@ WHERE NULLIF(b."String", 'Seattle') = 'London' """); } + public override async Task Conditional_uncoalesce_is_null() + { + await base.Conditional_uncoalesce_is_null(); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" = 8 +"""); + } + + public override async Task Conditional_uncoalesce_is_not_null_with_nullable_column() + { + await base.Conditional_uncoalesce_is_not_null_with_nullable_column(); + + AssertSql( + """ +SELECT n."Id", n."Bool", n."Byte", n."ByteArray", n."DateOnly", n."DateTime", n."DateTimeOffset", n."Decimal", n."Double", n."Enum", n."FlagsEnum", n."Float", n."Guid", n."Int", n."Long", n."Short", n."String", n."TimeOnly", n."TimeSpan" +FROM "NullableBasicTypesEntities" AS n +WHERE NULLIF(n."Int", 8) IS NOT NULL +"""); + } + + public override async Task Conditional_uncoalesce_with_coalesce() + { + await base.Conditional_uncoalesce_with_coalesce(); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE COALESCE(NULLIF(b."Int", 0), -1) < 0 +"""); + } + public override async Task Coalesce() { await base.Coalesce(); diff --git a/test/EFCore.PG.Tests/Migrations/JsonIndexTest.cs b/test/EFCore.PG.Tests/Migrations/JsonIndexTest.cs index 6fef0bc5c..b456d5f17 100644 --- a/test/EFCore.PG.Tests/Migrations/JsonIndexTest.cs +++ b/test/EFCore.PG.Tests/Migrations/JsonIndexTest.cs @@ -12,55 +12,52 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Migrations; public class JsonIndexTest { [Fact] - public void Member_index_migration_scaffolding_fails_on_rc2() + public void Member_index_migration_scaffolds_and_generates_sql() { - using var context = new JsonIndexContext(b => b.HasIndex(e => e.Details.Slug)); + using var context = new JsonIndexContext(b => b.HasIndex(e => e.Details.Slug).IsUnique()); var model = context.GetService().Model; - var operations = context.GetService().GetDifferences(null, model.GetRelationalModel()); + var operation = Assert.Single( + context.GetService().GetDifferences(null, model.GetRelationalModel()) + .OfType()); using var services = CreateDesignTimeServices(context); + var generator = services.GetRequiredService(); + var source = generator.GenerateMigration("JsonIndexSnapshot", "AddJsonIndex", [operation], []); + var metadata = generator.GenerateMetadata( + "JsonIndexSnapshot", typeof(JsonIndexContext), "AddJsonIndex", "20261111000000_AddJsonIndex", model); - // Replace with successful scaffolding after https://github.com/dotnet/efcore/issues/39064. - // Fixed by https://github.com/dotnet/efcore/pull/39089. - Assert.Equal( - "Cannot scaffold C# literals of type 'Microsoft.EntityFrameworkCore.Metadata.RelationalJsonIndex'. " - + "The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.", - Assert.Throws( - () => services.GetRequiredService() - .GenerateMigration("JsonIndexSnapshot", "Migration", operations, [])).Message); + Assert.DoesNotContain(RelationalAnnotationNames.JsonIndex, source); + + CompileAndAssert( + [source, metadata], assembly => + { + var migration = (Migration)Activator.CreateInstance(assembly.GetType("JsonIndexSnapshot.AddJsonIndex")); + Assert.Null(Assert.Single(migration.UpOperations)[RelationalAnnotationNames.JsonIndex]); + var targetModel = context.GetService().Initialize(migration.TargetModel, designTime: true); + Assert.Equal( + """CREATE UNIQUE INDEX "IX_Blogs_Details_Slug" ON "Blogs" (("Details" ->> 'Slug'));""", + Assert.Single(context.GetService().Generate(migration.UpOperations, targetModel)) + .CommandText.TrimEnd()); + }); } [Theory] + [InlineData(false, false)] [InlineData(false, true)] [InlineData(true, false)] - public void Mixed_composite_directions_fail_on_rc2(bool first, bool second) + [InlineData(true, true)] + public void Composite_sort_directions(bool first, bool second) { using var context = new JsonIndexContext( b => b.HasIndex(e => new { e.Details.Slug, e.Details.Rating }).IsDescending(first, second)); var model = context.GetService().Model; - - // Replace with successful index generation after https://github.com/dotnet/efcore/issues/39064. - // Fixed by https://github.com/dotnet/efcore/pull/39089. - Assert.Equal( - "2 values were provided in CreateIndexOperations.IsDescending, but the operation has 1 columns.", - Assert.Throws( - () => context.GetService().GetDifferences(null, model.GetRelationalModel())).Message); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Uniform_composite_directions_work(bool descending) - { - using var context = new JsonIndexContext( - b => b.HasIndex(e => new { e.Details.Slug, e.Details.Rating }).IsDescending(descending, descending)); - var model = context.GetService().Model; var operation = Assert.Single( context.GetService().GetDifferences(null, model.GetRelationalModel()) .OfType()); - var direction = descending ? " DESC" : ""; + var firstDirection = first ? " DESC" : ""; + var secondDirection = second ? " DESC" : ""; Assert.Equal( - $"""CREATE INDEX "IX_Blogs_Details_Slug_Details_Rating" ON "Blogs" (("Details" ->> 'Slug'){direction}, (CAST("Details" ->> 'Rating' AS integer)){direction});""", + $"""CREATE INDEX "IX_Blogs_Details_Slug_Details_Rating" ON "Blogs" (("Details" ->> 'Slug'){firstDirection}, (CAST("Details" ->> 'Rating' AS integer)){secondDirection});""", Assert.Single(context.GetService().Generate([operation], model)).CommandText.TrimEnd()); } @@ -76,8 +73,6 @@ public void Whole_document_index_migration_still_scaffolds(string property) var operation = Assert.Single( context.GetService().GetDifferences(null, model.GetRelationalModel()) .OfType()); - Assert.Null(operation[RelationalAnnotationNames.JsonIndex]); - Assert.Null(operation[NpgsqlAnnotationNames.JsonIndexTypes]); using var services = CreateDesignTimeServices(context); var source = services.GetRequiredService() @@ -121,16 +116,19 @@ public void Legacy_document_index_migration_still_scaffolds(Type clrType) } [Theory] - [InlineData(false)] - [InlineData(true)] - public void Member_index_snapshot_round_trips(bool composite) + [InlineData(false, false, false)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + [InlineData(true, true, true)] + public void Member_index_snapshot_round_trips(bool composite, bool first, bool second) { using var context = new JsonIndexContext( b => { if (composite) { - b.HasIndex(e => new { e.Details.Slug, e.Details.Rating }); + b.HasIndex(e => new { e.Details.Slug, e.Details.Rating }).IsDescending(first, second); } else { @@ -141,33 +139,15 @@ public void Member_index_snapshot_round_trips(bool composite) using var services = CreateDesignTimeServices(context); var source = services.GetRequiredService() .GenerateSnapshot("JsonIndexSnapshot", typeof(JsonIndexContext), "Snapshot", model); - var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")).Split(Path.PathSeparator) - .Append(typeof(JsonIndexTest).Assembly.Location) - .Distinct() - .Select(path => MetadataReference.CreateFromFile(path)); - var compilation = CSharpCompilation.Create( - "JsonIndexSnapshot", - [CSharpSyntaxTree.ParseText(source)], - references, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - using var stream = new MemoryStream(); - var result = compilation.Emit(stream); - Assert.True(result.Success, string.Join(Environment.NewLine, result.Diagnostics)); - stream.Position = 0; - var loadContext = new AssemblyLoadContext("JsonIndexSnapshot", isCollectible: true); - try - { - var snapshot = (ModelSnapshot)Activator.CreateInstance( - loadContext.LoadFromStream(stream).GetType("JsonIndexSnapshot.Snapshot")!); - var snapshotModel = context.GetService().Initialize(snapshot.Model, designTime: true); - Assert.Empty( - context.GetService() - .GetDifferences(model.GetRelationalModel(), snapshotModel.GetRelationalModel())); - } - finally - { - loadContext.Unload(); - } + CompileAndAssert( + [source], assembly => + { + var snapshot = (ModelSnapshot)Activator.CreateInstance(assembly.GetType("JsonIndexSnapshot.Snapshot")); + var snapshotModel = context.GetService().Initialize(snapshot.Model, designTime: true); + Assert.Empty( + context.GetService() + .GetDifferences(model.GetRelationalModel(), snapshotModel.GetRelationalModel())); + }); } [Fact] @@ -240,6 +220,32 @@ private static ServiceProvider CreateDesignTimeServices(DbContext context) return services.BuildServiceProvider(); } + private static void CompileAndAssert(string[] sources, Action assert) + { + var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")).Split(Path.PathSeparator) + .Append(typeof(JsonIndexTest).Assembly.Location) + .Distinct() + .Select(path => MetadataReference.CreateFromFile(path)); + var compilation = CSharpCompilation.Create( + "JsonIndexSnapshot", + sources.Select(source => CSharpSyntaxTree.ParseText(source)), + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + using var stream = new MemoryStream(); + var result = compilation.Emit(stream); + Assert.True(result.Success, string.Join(Environment.NewLine, result.Diagnostics)); + stream.Position = 0; + var loadContext = new AssemblyLoadContext("JsonIndexSnapshot", isCollectible: true); + try + { + assert(loadContext.LoadFromStream(stream)); + } + finally + { + loadContext.Unload(); + } + } + public class JsonIndexContext(Action> configureIndex) : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)