diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs index beb5daac8b9..fb674031523 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs @@ -60,6 +60,13 @@ protected virtual void ValidateSharedContainerCompatibility( continue; } + var ownership = entityType.FindOwnership(); + if (ownership != null) + { + throw new InvalidOperationException(CosmosStrings.OwnedTypeDifferentContainer( + entityType.DisplayName(), ownership.PrincipalEntityType.DisplayName(), container)); + } + if (!containers.TryGetValue(container, out var mappedTypes)) { mappedTypes = new List(); diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs index 9523f38b1be..197e81d7a42 100644 --- a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs +++ b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs @@ -227,6 +227,14 @@ public static string OrphanedNestedDocumentSensitive(object? entityType, object? GetString("OrphanedNestedDocumentSensitive", nameof(entityType), nameof(missingEntityType), nameof(keyValue)), entityType, missingEntityType, keyValue); + /// + /// The entity type '{entityType}' is owned by the entity type '{owner}', but is mapped to the container '{container}'. Owned types mapped to a container directly are not supported, remove this configuration to allow the owned type to be embedded in the same document as the owner. + /// + public static string OwnedTypeDifferentContainer(object? entityType, object? owner, object? container) + => string.Format( + GetString("OwnedTypeDifferentContainer", nameof(entityType), nameof(owner), nameof(container)), + entityType, owner, container); + /// /// The partition key specified in the 'WithPartitionKey' call '{partitionKey1}' and the partition key specified in the 'Where' predicate '{partitionKey2}' must be identical to return any results. Remove one of them. /// diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.resx b/src/EFCore.Cosmos/Properties/CosmosStrings.resx index 99ccab5273c..314782e698f 100644 --- a/src/EFCore.Cosmos/Properties/CosmosStrings.resx +++ b/src/EFCore.Cosmos/Properties/CosmosStrings.resx @@ -226,6 +226,9 @@ The entity of type '{entityType}' is mapped as part of the document mapped to '{missingEntityType}', but there is no tracked entity of this type with the key value '{keyValue}'. + + The entity type '{entityType}' is owned by the entity type '{owner}', but is mapped to the container '{container}'. Owned types mapped to a container directly are not supported, remove this configuration to allow the owned type to be embedded in the same document as the owner. + The partition key specified in the 'WithPartitionKey' call '{partitionKey1}' and the partition key specified in the 'Where' predicate '{partitionKey2}' must be identical to return any results. Remove one of them. diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 3730b403b31..f52031bfe67 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -299,7 +299,7 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy RelationalStrings.StoredProcedureKeyless( entityType.DisplayName(), storeObjectIdentifier.DisplayName())); } - + var properties = entityType.GetDeclaredProperties().ToDictionary(p => p.Name); if (mappingStrategy == RelationalAnnotationNames.TphMappingStrategy) { @@ -339,12 +339,12 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy { break; } - + foreach (var property in baseType.GetDeclaredProperties()) { properties.Add(property.Name, property); } - + baseType = baseType.BaseType; } } @@ -372,14 +372,14 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy RelationalStrings.StoredProcedureResultColumnNotFound( resultColumn.PropertyName, entityType.DisplayName(), storeObjectIdentifier.DisplayName())); } - + if (!resultColumnNames.Add(resultColumn.Name)) { throw new InvalidOperationException( RelationalStrings.StoredProcedureDuplicateResultColumnName( resultColumn.Name, storeObjectIdentifier.DisplayName())); } - + if (resultColumn.PropertyName == null) { continue; @@ -406,7 +406,7 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy break; } } - + var parameterNames = new HashSet(); foreach (var parameter in sproc.Parameters) { @@ -429,14 +429,14 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy parameter.PropertyName, entityType.DisplayName(), storeObjectIdentifier.DisplayName())); } } - + if (!parameterNames.Add(parameter.Name)) { throw new InvalidOperationException( RelationalStrings.StoredProcedureDuplicateParameterName( parameter.Name, storeObjectIdentifier.DisplayName())); } - + if (parameter.PropertyName == null) { continue; @@ -472,7 +472,7 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy RelationalStrings.StoredProcedureDeleteNonKeyProperty( entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName())); } - + break; default: Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType); @@ -486,9 +486,9 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy { continue; } - + properties.Remove(resultColumn.PropertyName); - + if (!storeGeneratedProperties.Remove(resultColumn.PropertyName)) { throw new InvalidOperationException( @@ -496,7 +496,7 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy entityType.DisplayName(), resultColumn.PropertyName, storeObjectIdentifier.DisplayName())); } } - + if (storeGeneratedProperties.Count > 0) { throw new InvalidOperationException( @@ -1098,7 +1098,7 @@ protected virtual void ValidateSharedColumnsCompatibility( { continue; } - + missingConcurrencyTokens?.Remove(columnName); if (!propertyMappings.TryGetValue(columnName, out var duplicateProperty)) { @@ -1700,7 +1700,7 @@ protected override void ValidateInheritanceMapping( RelationalStrings.AbstractTpc(entityType.DisplayName(), storeObject)); } } - + foreach (var key in entityType.GetKeys()) { ValidateValueGeneration(entityType, key, logger); @@ -1739,12 +1739,12 @@ protected override void ValidateInheritanceMapping( ValidateTphMapping(entityType, StoreObjectType.InsertStoredProcedure); ValidateTphMapping(entityType, StoreObjectType.DeleteStoredProcedure); ValidateTphMapping(entityType, StoreObjectType.UpdateStoredProcedure); - + ValidateDiscriminatorValues(entityType); } else { - if (mappingStrategy != RelationalAnnotationNames.TpcMappingStrategy + if (mappingStrategy != RelationalAnnotationNames.TpcMappingStrategy && entityType.FindPrimaryKey() == null) { throw new InvalidOperationException( @@ -1957,7 +1957,7 @@ private static void ValidateTphMapping(IEntityType rootEntityType, StoreObjectTy protected override bool IsRedundant(IForeignKey foreignKey) => base.IsRedundant(foreignKey) && !foreignKey.DeclaringEntityType.GetMappingFragments().Any(); - + /// /// Validates the entity type mapping fragments. /// @@ -2027,7 +2027,7 @@ protected virtual void ValidateMappingFragments( continue; } - + if (!property.IsPrimaryKey()) { propertiesFound = true; @@ -2218,7 +2218,7 @@ private static IEnumerable GetAllMappedStoreObjects( { yield return declaringStoreObject.Value; } - + foreach (var fragment in fragments) { overrides = RelationalPropertyOverrides.Find(property, fragment.StoreObject); @@ -2261,7 +2261,7 @@ private static IEnumerable GetAllMappedStoreObjects( queue.Enqueue(containingType); } } - } + } } } diff --git a/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs b/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs index 239c7ff8f26..4979fc53b29 100644 --- a/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs +++ b/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs @@ -19,12 +19,12 @@ public interface IStoreStoredProcedure : ITableBase /// Gets the entity type mappings. /// new IEnumerable EntityTypeMappings { get; } - + /// /// Gets the return for this stored procedure. /// IStoreStoredProcedureReturnValue? ReturnValue { get; } - + /// /// Gets the parameters for this stored procedure. /// @@ -81,7 +81,7 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt builder .Append(indentString) .Append("StoreStoredProcedure: "); - + if (Schema != null) { builder diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs index 614c2c8e4af..33ee281234a 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs @@ -36,7 +36,7 @@ public StoreStoredProcedure(IRuntimeStoredProcedure sproc, RelationalModel model /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual SortedSet StoredProcedures { get; } - + /// /// 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.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs index deb8a117c46..99fce63f1b9 100644 --- a/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs +++ b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs @@ -273,6 +273,23 @@ public virtual void Detects_property_and_embedded_type_mapped_to_same_property() nameof(Order.OrderDetails), nameof(Order.PartitionId), typeof(Order).Name, "Details"), modelBuilder); } + [ConditionalFact] + public virtual void Detects_owned_type_mapped_to_a_container() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity(); + modelBuilder.Entity( + ob => + { + var ownedType = ob.OwnsOne(o => o.OrderDetails).OwnedEntityType; + ownedType.SetContainer("Details"); + }); + + VerifyError( + CosmosStrings.OwnedTypeDifferentContainer( + nameof(OrderDetails), nameof(Order), "Details"), modelBuilder); + } + [ConditionalFact] public virtual void Detects_missing_discriminator() {