diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index 91d50924a0e..cb4f7f2868d 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -1279,11 +1279,23 @@ private void InitializeJsonColumn( columnOperation.ClrType = typeof(string); columnOperation.DefaultValue = inline || isNullable ? null - : "{}"; + : IsJsonCollectionColumn(jsonColumn) ? "[]" : "{}"; columnOperation.AddAnnotations(migrationsAnnotations); } + private static bool IsJsonCollectionColumn(JsonColumn jsonColumn) + => jsonColumn.Table.ComplexTypeMappings.Any( + m => m.TypeBase is IComplexType ct + && ct.GetContainerColumnName() == jsonColumn.Name + && ct.ComplexProperty.IsCollection + && !ct.ComplexProperty.DeclaringType.IsMappedToJson()) + || jsonColumn.Table.EntityTypeMappings.Any( + m => m.TypeBase is IEntityType et + && et.GetContainerColumnName() == jsonColumn.Name + && et.FindOwnership() is { IsUnique: false, PrincipalEntityType: var principal } + && !principal.IsMappedToJson()); + #endregion #region IKey diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index aed949d96a0..758128b0789 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -10007,6 +10007,202 @@ public virtual void Convert_table_from_owned_to_complex_properties_mapped_to_jso Assert.Empty); #pragma warning restore EF8001 // Owned JSON entities are obsolete + [ConditionalFact] + public virtual void Add_complex_collection_mapped_to_json_uses_empty_array_as_default_value() + => Execute( + _ => { }, + source => + { + source.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + }); + }, + target => + { + target.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + + e.ComplexCollection, MyJsonComplex>( + "ComplexCollection", cp => + { + cp.IsRequired(); + cp.ToJson("json_collection"); + cp.Property(x => x.Value); + cp.Property(x => x.Date); + }); + }); + }, + upOps => + { + Assert.Equal(1, upOps.Count); + + var operation = Assert.IsType(upOps[0]); + Assert.Equal("Entity", operation.Table); + Assert.Equal("json_collection", operation.Name); + Assert.Equal("[]", operation.DefaultValue); + }, + downOps => + { + Assert.Equal(1, downOps.Count); + Assert.IsType(downOps[0]); + }); + + [ConditionalFact] + public virtual void Add_complex_reference_with_nested_collection_mapped_to_json_uses_empty_object_as_default_value() + => Execute( + _ => { }, + source => + { + source.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + }); + }, + target => + { + target.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + + e.ComplexProperty( + "ComplexReference", cp => + { + cp.IsRequired(); + cp.ToJson("json_reference"); + cp.Property(x => x.Value); + cp.Property(x => x.Date); + cp.ComplexCollection( + x => x.NestedCollection, nc => { }); + }); + }); + }, + upOps => + { + Assert.Equal(1, upOps.Count); + + var operation = Assert.IsType(upOps[0]); + Assert.Equal("Entity", operation.Table); + Assert.Equal("json_reference", operation.Name); + Assert.Equal("{}", operation.DefaultValue); + }, + downOps => + { + Assert.Equal(1, downOps.Count); + Assert.IsType(downOps[0]); + }); + +#pragma warning disable EF8001 // Owned JSON entities are obsolete + [ConditionalFact] + public virtual void Add_owned_collection_mapped_to_json_has_nullable_column() + => Execute( + _ => { }, + source => + { + source.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + }); + }, + target => + { + target.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + + e.OwnsMany( + "Owned", "json_collection", o => + { + o.ToJson(); + o.Property("Value"); + o.Property("Date"); + }); + }); + }, + upOps => + { + Assert.Equal(1, upOps.Count); + + var operation = Assert.IsType(upOps[0]); + Assert.Equal("Entity", operation.Table); + Assert.Equal("json_collection", operation.Name); + // Owned collections are always nullable (IsUnique is false), so no default value + Assert.True(operation.IsNullable); + Assert.Null(operation.DefaultValue); + }, + downOps => + { + Assert.Equal(1, downOps.Count); + Assert.IsType(downOps[0]); + }); +#pragma warning restore EF8001 // Owned JSON entities are obsolete + +#pragma warning disable EF8001 // Owned JSON entities are obsolete + [ConditionalFact] + public virtual void Add_owned_reference_with_nested_collection_mapped_to_json_uses_empty_object_as_default_value() + => Execute( + _ => { }, + source => + { + source.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + }); + }, + target => + { + target.Entity( + "Entity", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.HasKey("Id"); + + e.OwnsOne( + "Owned", "json_reference", o => + { + o.ToJson(); + o.Property("Value"); + o.OwnsMany( + "Nested", "NestedCollection", n => + { + n.Property("Number"); + }); + }); + + e.Navigation("json_reference").IsRequired(); + }); + }, + upOps => + { + Assert.Equal(1, upOps.Count); + + var operation = Assert.IsType(upOps[0]); + Assert.Equal("Entity", operation.Table); + Assert.Equal("json_reference", operation.Name); + Assert.Equal("{}", operation.DefaultValue); + }, + downOps => + { + Assert.Equal(1, downOps.Count); + Assert.IsType(downOps[0]); + }); +#pragma warning restore EF8001 // Owned JSON entities are obsolete + [ConditionalFact] public virtual void Noop_on_complex_properties() => Execute(