diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs index 130041d556d..6d57670d666 100644 --- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs +++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs @@ -76,11 +76,10 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont var computedIdProperty = entityType.FindDeclaredProperty(DefaultIdPropertyName); var primaryKey = entityType.FindPrimaryKey(); - if (entityType.BaseType != null // Requires: IEntityTypeBaseTypeChangedConvention - || !entityType.IsDocumentRoot() // Requires: IEntityTypeAnnotationChangedConvention (ContainerName) - || entityType.GetForeignKeys() - .Any(fk => fk.IsOwnership) // Requires: IForeignKeyOwnershipChangedConvention, IForeignKeyRemovedConvention - || primaryKey == null) // Requires: IKeyAddedConvention, IKeyRemovedConvention + if (entityType.BaseType != null + || !entityType.IsDocumentRoot() + || entityType.GetForeignKeys().Any(fk => fk.IsOwnership) + || primaryKey == null) { // If the entity type is not a keyed, root document in the container, then it doesn't have an `id` mapping, so // undo anything that was done by previous execution of this convention. @@ -93,7 +92,8 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont if (computedIdProperty is not null && computedIdProperty != jsonIdProperty) { - entityType.Builder.RemoveUnusedImplicitProperties([computedIdProperty]); } + entityType.Builder.RemoveUnusedImplicitProperties([computedIdProperty]); + } return; } @@ -102,17 +102,9 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont // key is represented by a single string property, and the discriminator is not being included in the JSON `id`. // If these conditions are not met, or if the user has opted-in, then we will create a computed property that transforms // the appropriate values into a single string for the JSON `id` property. - - // The line below requires: IModelAnnotationChangedConvention, IPropertyAnnotationChangedConvention var alwaysCreateId = entityType.GetHasShadowId(); if (alwaysCreateId != true) { - // The line below requires: - // - IModelAnnotationChangedConvention, IPropertyAnnotationChangedConvention - // - IKeyAddedConvention, IKeyRemovedConvention - // - IPropertyAddedConvention, IPropertyRemovedConvention - // - IDiscriminatorPropertySetConvention - // - IEntityTypeBaseTypeChangedConvention var idDefinition = DefinitionFactory.Create((IEntityType)entityType)!; if (idDefinition is { IncludesDiscriminator: false, Properties.Count: 1 }) { @@ -183,13 +175,10 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont return; } - if (computedIdPropertyBuilder.Metadata.GetJsonPropertyName() != IdPropertyJsonName) - { - computedIdPropertyBuilder = computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName) - ?? computedIdPropertyBuilder; - } - // Don't chain, because each of these could return null if the property has been explicitly configured with some other value. + computedIdPropertyBuilder = computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName) + ?? computedIdPropertyBuilder; + computedIdPropertyBuilder = computedIdPropertyBuilder.IsRequired(true) ?? computedIdPropertyBuilder; diff --git a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs index 0865ab00f6d..418d51dbfd2 100644 --- a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs @@ -230,7 +230,7 @@ public virtual void Hierarchical_partition_key_is_added_to_the_alternate_key_if_ { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey(CosmosJsonIdConvention.DefaultIdPropertyName); modelBuilder.Entity() @@ -311,7 +311,7 @@ public virtual void No_alternate_key_is_created_if_primary_key_contains_id() { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey(CosmosJsonIdConvention.DefaultIdPropertyName); modelBuilder.Entity() @@ -335,7 +335,7 @@ public virtual void No_alternate_key_is_created_if_primary_key_contains_id_and_p { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey(nameof(Customer.AlternateKey), CosmosJsonIdConvention.DefaultIdPropertyName); modelBuilder.Entity() @@ -359,7 +359,7 @@ public virtual void No_alternate_key_is_created_if_primary_key_contains_id_and_h { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey( nameof(Customer.AlternateKey), @@ -404,7 +404,7 @@ public virtual void No_alternate_key_is_created_if_primary_key_contains_id_and_h { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey( nameof(Customer.Title), @@ -449,7 +449,7 @@ public virtual void Hierarchical_partition_key_is_added_to_the_alternate_key_if_ { var modelBuilder = CreateModelBuilder(); - modelBuilder.Entity().AlwaysHasShadowId(); + modelBuilder.Entity().HasShadowId(); modelBuilder.Entity().HasKey( nameof(Customer.Title), diff --git a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosTestModelBuilderExtensions.cs b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosTestModelBuilderExtensions.cs index 591f834139a..0ca33ed6cb9 100644 --- a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosTestModelBuilderExtensions.cs +++ b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosTestModelBuilderExtensions.cs @@ -7,37 +7,49 @@ namespace Microsoft.EntityFrameworkCore.ModelBuilding; public static class CosmosTestModelBuilderExtensions { - public static ModelBuilderTest.TestEntityTypeBuilder HasPartitionKey( + public static ModelBuilderTest.TestModelBuilder HasShadowIds( + this ModelBuilderTest.TestModelBuilder builder, + bool? alwaysCreate = true) + { + if (builder is IInfrastructure nonGenericBuilder) + { + nonGenericBuilder.Instance.HasShadowIds(alwaysCreate); + } + + return builder; + } + + public static ModelBuilderTest.TestEntityTypeBuilder HasShadowId( this ModelBuilderTest.TestEntityTypeBuilder builder, - Expression> propertyExpression) + bool? alwaysCreate = true) where TEntity : class { switch (builder) { case IInfrastructure> genericBuilder: - genericBuilder.Instance.HasPartitionKey(propertyExpression); + genericBuilder.Instance.HasShadowId(alwaysCreate); break; case IInfrastructure nonGenericBuilder: - var names = propertyExpression.GetMemberAccessList().Select(e => e.GetSimpleMemberName()).ToList(); - nonGenericBuilder.Instance.HasPartitionKey(names.FirstOrDefault(), names.Count > 1 ? names.Skip(1).ToArray() : []); + nonGenericBuilder.Instance.HasShadowId(alwaysCreate); break; } return builder; } - public static ModelBuilderTest.TestEntityTypeBuilder AlwaysHasShadowId( + public static ModelBuilderTest.TestEntityTypeBuilder HasPartitionKey( this ModelBuilderTest.TestEntityTypeBuilder builder, - bool? alwaysCreate = true) + Expression> propertyExpression) where TEntity : class { switch (builder) { case IInfrastructure> genericBuilder: - genericBuilder.Instance.HasShadowId(alwaysCreate); + genericBuilder.Instance.HasPartitionKey(propertyExpression); break; case IInfrastructure nonGenericBuilder: - nonGenericBuilder.Instance.HasShadowId(alwaysCreate); + var names = propertyExpression.GetMemberAccessList().Select(e => e.GetSimpleMemberName()).ToList(); + nonGenericBuilder.Instance.HasPartitionKey(names.FirstOrDefault(), names.Count > 1 ? names.Skip(1).ToArray() : []); break; } @@ -63,6 +75,40 @@ public static ModelBuilderTest.TestEntityTypeBuilder HasPartitionKey ToContainer( + this ModelBuilderTest.TestEntityTypeBuilder builder, + string name) + where TEntity : class + { + switch (builder) + { + case IInfrastructure> genericBuilder: + genericBuilder.Instance.ToContainer(name); + break; + case IInfrastructure nonGenericBuilder: + nonGenericBuilder.Instance.ToContainer(name); + break; + } + + return builder; + } + public static ModelBuilderTest.TestEntityTypeBuilder UseETagConcurrency( + this ModelBuilderTest.TestEntityTypeBuilder builder) + where TEntity : class + { + switch (builder) + { + case IInfrastructure> genericBuilder: + genericBuilder.Instance.UseETagConcurrency(); + break; + case IInfrastructure nonGenericBuilder: + nonGenericBuilder.Instance.UseETagConcurrency(); + break; + } + + return builder; + } + public static ModelBuilderTest.TestPropertyBuilder ToJsonProperty( this ModelBuilderTest.TestPropertyBuilder builder, string name)