diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs index ad0dcabff9e..f162fb711ef 100644 --- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs +++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs @@ -72,8 +72,12 @@ public static void SetContainer(this IMutableEntityType entityType, string? name /// The entity type to get the containing property name for. /// The name of the parent property to which the entity type is mapped. public static string? GetContainingPropertyName(this IReadOnlyEntityType entityType) - => entityType[CosmosAnnotationNames.PropertyName] as string - ?? GetDefaultContainingPropertyName(entityType); + { + var propertyName = entityType.FindAnnotation(CosmosAnnotationNames.PropertyName); + return propertyName == null + ? GetDefaultContainingPropertyName(entityType) + : (string?)propertyName.Value; + } private static string? GetDefaultContainingPropertyName(IReadOnlyEntityType entityType) => entityType.FindOwnership() is IReadOnlyForeignKey ownership @@ -198,7 +202,7 @@ public static void SetPartitionKeyPropertyName(this IMutableEntityType entityTyp public static IReadOnlyList GetPartitionKeyPropertyNames(this IReadOnlyEntityType entityType) => entityType[CosmosAnnotationNames.PartitionKeyNames] as IReadOnlyList ?? entityType.BaseType?.GetPartitionKeyPropertyNames() - ?? Array.Empty(); + ?? []; /// /// Sets the names of the properties that are used to store the hierarchical partition key. diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 93beae7be86..8cee65a5187 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -1606,8 +1606,12 @@ public static void SetContainerColumnName(this IMutableEntityType entityType, st /// The entity type to get the container column name for. /// The container column name to which the entity type is mapped. public static string? GetContainerColumnName(this IReadOnlyEntityType entityType) - => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value as string - ?? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName(); + { + var containerColumnName = entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName); + return containerColumnName == null + ? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName() + : (string?)containerColumnName.Value; + } /// /// Sets the column type to use for the container column to which the entity type is mapped. @@ -1720,8 +1724,14 @@ public static void SetContainerColumnTypeMapping(this IMutableEntityType entityT /// is returned for entities that are not mapped to a JSON column. /// public static string? GetJsonPropertyName(this IReadOnlyEntityType entityType) - => (string?)entityType.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value - ?? (!entityType.IsMappedToJson() ? null : entityType.FindOwnership()!.GetNavigation(pointsToPrincipal: false)!.Name); + { + var propertyName = entityType.FindAnnotation(RelationalAnnotationNames.JsonPropertyName); + return propertyName == null + ? (entityType.IsMappedToJson() + ? entityType.FindOwnership()!.GetNavigation(pointsToPrincipal: false)!.Name + : null) + : (string?)propertyName.Value; + } /// /// Sets the value of JSON property name used for the given entity mapped to a JSON column. diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index ebd72d633df..69ed1618137 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -2833,8 +2833,13 @@ protected virtual void ValidateJsonEntityProperties( IEntityType jsonEntityType) { var jsonPropertyNames = new List(); - foreach (var property in jsonEntityType.GetDeclaredProperties().Where(p => !string.IsNullOrEmpty(p.GetJsonPropertyName()))) + foreach (var property in jsonEntityType.GetDeclaredProperties()) { + if (string.IsNullOrEmpty(property.GetJsonPropertyName())) + { + continue; + } + if (property.TryGetDefaultValue(out var _)) { throw new InvalidOperationException( @@ -2857,6 +2862,12 @@ protected virtual void ValidateJsonEntityProperties( foreach (var navigation in jsonEntityType.GetDeclaredNavigations()) { + if (!navigation.TargetEntityType.IsMappedToJson() + || navigation.IsOnDependent) + { + continue; + } + var jsonPropertyName = navigation.TargetEntityType.GetJsonPropertyName()!; if (!jsonPropertyNames.Contains(jsonPropertyName)) { diff --git a/test/EFCore.SqlServer.FunctionalTests/ModelBuilding/SqlServerModelBuilderTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/ModelBuilding/SqlServerModelBuilderTestBase.cs index 5f76da6288e..9b20c495e9e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/ModelBuilding/SqlServerModelBuilderTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/ModelBuilding/SqlServerModelBuilderTestBase.cs @@ -1633,8 +1633,11 @@ public virtual void Json_entity_with_nested_structure_same_property_names() x => x.OwnedCollection2, bb => { bb.ToJson("col2"); - bb.OwnsOne(x => x.Reference1); - bb.OwnsOne(x => x.Reference2); + bb.OwnsOne(x => x.Reference1) + .HasAnnotation(RelationalAnnotationNames.JsonPropertyName, null); + bb.OwnsOne(x => x.Reference2) + .ToTable("Ref2") + .HasAnnotation(RelationalAnnotationNames.ContainerColumnName, null); bb.OwnsMany(x => x.Collection1); bb.OwnsMany(x => x.Collection2); }); @@ -1642,36 +1645,49 @@ public virtual void Json_entity_with_nested_structure_same_property_names() var model = modelBuilder.FinalizeModel(); var outerOwnedEntities = model.FindEntityTypes(typeof(OwnedEntityExtraLevel)); - Assert.Equal(4, outerOwnedEntities.Count()); + + Assert.Collection(outerOwnedEntities, + e => Assert.Equal("col1", e.GetContainerColumnName()), + e => Assert.Equal("col2", e.GetContainerColumnName()), + e => Assert.Equal("ref1", e.GetContainerColumnName()), + e => Assert.Equal("ref2", e.GetContainerColumnName())); foreach (var outerOwnedEntity in outerOwnedEntities) { Assert.Equal("Date", outerOwnedEntity.GetProperty("Date").GetJsonPropertyName()); Assert.Equal("Fraction", outerOwnedEntity.GetProperty("Fraction").GetJsonPropertyName()); Assert.Equal("Enum", outerOwnedEntity.GetProperty("Enum").GetJsonPropertyName()); - Assert.Equal( - "Reference1", - outerOwnedEntity.GetNavigations().Single(n => n.Name == "Reference1").TargetEntityType.GetJsonPropertyName()); - Assert.Equal( - "Reference2", - outerOwnedEntity.GetNavigations().Single(n => n.Name == "Reference2").TargetEntityType.GetJsonPropertyName()); - Assert.Equal( - "Collection1", - outerOwnedEntity.GetNavigations().Single(n => n.Name == "Collection1").TargetEntityType.GetJsonPropertyName()); - Assert.Equal( - "Collection2", - outerOwnedEntity.GetNavigations().Single(n => n.Name == "Collection2").TargetEntityType.GetJsonPropertyName()); - } - var ownedEntities = model.FindEntityTypes(typeof(OwnedEntity)); - Assert.Equal(16, ownedEntities.Count()); + var nestedOwnedTypes = outerOwnedEntity.GetNavigations().Select(n => n.TargetEntityType).ToList(); + Assert.Collection(nestedOwnedTypes, + e => Assert.Equal("Collection1", e.GetJsonPropertyName()), + e => Assert.Equal("Collection2", e.GetJsonPropertyName()), + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName() == "col2" ? null : "Reference1", + e.GetJsonPropertyName()), + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName() == "col2" ? null : "Reference2", + e.GetJsonPropertyName())); + + Assert.Collection(nestedOwnedTypes, + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName(), e.GetContainerColumnName()), + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName(), e.GetContainerColumnName()), + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName(), e.GetContainerColumnName()), + e => Assert.Equal(outerOwnedEntity.GetContainerColumnName() == "col2" ? + null : outerOwnedEntity.GetContainerColumnName(), e.GetContainerColumnName())); + + foreach (var ownedEntity in nestedOwnedTypes) + { + if (ownedEntity.GetContainerColumnName() == null) + { + continue; + } - foreach (var ownedEntity in ownedEntities) - { - Assert.Equal("Date", ownedEntity.GetProperty("Date").GetJsonPropertyName()); - Assert.Equal("Fraction", ownedEntity.GetProperty("Fraction").GetJsonPropertyName()); - Assert.Equal("Enum", ownedEntity.GetProperty("Enum").GetJsonPropertyName()); + Assert.Equal("Date", ownedEntity.GetProperty("Date").GetJsonPropertyName()); + Assert.Equal("Fraction", ownedEntity.GetProperty("Fraction").GetJsonPropertyName()); + Assert.Equal("Enum", ownedEntity.GetProperty("Enum").GetJsonPropertyName()); + } } + + Assert.Equal(16, model.FindEntityTypes(typeof(OwnedEntity)).Count()); } [ConditionalFact] @@ -2047,62 +2063,6 @@ public virtual void Json_entity_and_normal_owned_can_exist_side_to_side_on_same_ Assert.Equal(2, ownedEntities.Where(e => e.IsMappedToJson()).Count()); Assert.Equal(2, ownedEntities.Where(e => e.IsOwned() && !e.IsMappedToJson()).Count()); } - - [ConditionalFact] - public virtual void Json_entity_with_nested_structure_same_property_names_() - { - var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity( - b => - { - b.OwnsOne( - x => x.OwnedReference1, bb => - { - bb.ToJson("ref1"); - bb.OwnsOne(x => x.Reference1); - bb.OwnsOne(x => x.Reference2); - bb.OwnsMany(x => x.Collection1); - bb.OwnsMany(x => x.Collection2); - }); - - b.OwnsOne( - x => x.OwnedReference2, bb => - { - bb.ToJson("ref2"); - bb.OwnsOne(x => x.Reference1); - bb.OwnsOne(x => x.Reference2); - bb.OwnsMany(x => x.Collection1); - bb.OwnsMany(x => x.Collection2); - }); - - b.OwnsMany( - x => x.OwnedCollection1, bb => - { - bb.ToJson("col1"); - bb.OwnsOne(x => x.Reference1); - bb.OwnsOne(x => x.Reference2); - bb.OwnsMany(x => x.Collection1); - bb.OwnsMany(x => x.Collection2); - }); - - b.OwnsMany( - x => x.OwnedCollection2, bb => - { - bb.ToJson("col2"); - bb.OwnsOne(x => x.Reference1); - bb.OwnsOne(x => x.Reference2); - bb.OwnsMany(x => x.Collection1); - bb.OwnsMany(x => x.Collection2); - }); - }); - - var model = modelBuilder.FinalizeModel(); - var outerOwnedEntities = model.FindEntityTypes(typeof(OwnedEntityExtraLevel)); - Assert.Equal(4, outerOwnedEntities.Count()); - - var ownedEntities = model.FindEntityTypes(typeof(OwnedEntity)); - Assert.Equal(16, ownedEntities.Count()); - } } public class SqlServerModelBuilderFixture : RelationalModelBuilderFixture