diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 927366e1203..07c895f736e 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -707,8 +707,7 @@ protected virtual void GenerateComplexPropertyAnnotations( if (typeAnnotations.ContainsKey(RelationalAnnotationNames.ContainerColumnName) && !typeAnnotations.ContainsKey(RelationalAnnotationNames.ContainerColumnType)) { - var containerColumnType = property.ComplexType.GetContainerColumnType() - ?? Dependencies.RelationalTypeMappingSource.FindMapping(typeof(JsonTypePlaceholder))?.StoreType; + var containerColumnType = property.ComplexType.GetContainerColumnType(); if (containerColumnType != null) { typeAnnotations[RelationalAnnotationNames.ContainerColumnType] = new Annotation( @@ -921,8 +920,7 @@ protected virtual void GenerateEntityTypeAnnotations( if (annotations.ContainsKey(RelationalAnnotationNames.ContainerColumnName) && !annotations.ContainsKey(RelationalAnnotationNames.ContainerColumnType)) { - var containerColumnType = entityType.GetContainerColumnType() - ?? Dependencies.RelationalTypeMappingSource.FindMapping(typeof(JsonTypePlaceholder))?.StoreType; + var containerColumnType = entityType.GetContainerColumnType(); if (containerColumnType != null) { annotations[RelationalAnnotationNames.ContainerColumnType] = new Annotation( diff --git a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs index b5ee748ab77..89b67725b3a 100644 --- a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; // ReSharper disable once CheckNamespace #pragma warning disable IDE0130 // Namespace does not match folder structure @@ -416,11 +417,32 @@ public static void SetContainerColumnName(this IMutableTypeBase typeBase, string /// The type. /// The database column type. public static string? GetContainerColumnType(this IReadOnlyTypeBase typeBase) - => typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value is string columnName - ? columnName - : typeBase is IReadOnlyEntityType entityType - ? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnType() - : ((IReadOnlyComplexType)typeBase).ComplexProperty.DeclaringType.GetContainerColumnType(); + { + if (typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value is string columnType) + { + return columnType; + } + + var parentType = typeBase is IReadOnlyEntityType entityType + ? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnType() + : ((IReadOnlyComplexType)typeBase).ComplexProperty.DeclaringType.GetContainerColumnType(); + + if (parentType != null) + { + return parentType; + } + + if (typeBase.IsMappedToJson() +#pragma warning disable EF1001 // Internal EF Core API usage. + && (typeBase.Model is not Model model || model.IsReadOnly)) +#pragma warning restore EF1001 // Internal EF Core API usage. + { + return ((IRelationalTypeMappingSource)((IModel)typeBase.Model).GetModelDependencies().TypeMappingSource) + .FindMapping(typeof(JsonTypePlaceholder))?.StoreType; + } + + return null; + } /// /// Sets the type of the container column to which the type is mapped. diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs index 21c3f3d9cbc..9d6238784dd 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs @@ -3053,6 +3053,60 @@ public void Complex_collection_container_column_type_is_used_in_relational_model Assert.Equal("some_json_mapping", column.StoreType); } + [ConditionalFact] + public void Complex_property_gets_default_container_column_type_when_not_set_explicitly() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity(eb => + { + eb.ComplexProperty( + e => e.ComplexProperty, cb => + { + cb.ToJson("complex_data"); + }); + }); + + var model = Finalize(modelBuilder); + + var entityType = model.Model.FindEntityType(typeof(EntityWithComplexProperty)); + var complexProperty = entityType.GetComplexProperties().Single(); + var complexType = complexProperty.ComplexType; + + Assert.Equal("some_json_mapping", complexType.GetContainerColumnType()); + + var table = entityType.GetTableMappings().Single().Table; + var column = table.Columns.Single(c => c.Name == "complex_data"); + Assert.Equal("some_json_mapping", column.StoreType); + } + + [ConditionalFact] + public void Complex_collection_gets_default_container_column_type_when_not_set_explicitly() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity(eb => + { + eb.ComplexCollection( + e => e.ComplexCollection, cb => + { + cb.ToJson("collection_data"); + }); + }); + + var model = Finalize(modelBuilder); + + var entityType = model.Model.FindEntityType(typeof(EntityWithComplexCollection)); + var complexProperty = entityType.GetComplexProperties().Single(); + var complexType = complexProperty.ComplexType; + + Assert.Equal("some_json_mapping", complexType.GetContainerColumnType()); + + var table = entityType.GetTableMappings().Single().Table; + var column = table.Columns.Single(c => c.Name == "collection_data"); + Assert.Equal("some_json_mapping", column.StoreType); + } + [ConditionalFact] public void Can_use_relational_model_with_functions() {