Skip to content

Commit e667bf8

Browse files
authored
Get partition keys, container name, and JsonIdDefinition from the root entity in the hierarchy by default (#34252)
Fixes #34176 Also, model validation.
1 parent 92249e6 commit e667bf8

File tree

8 files changed

+111
-9
lines changed

8 files changed

+111
-9
lines changed

Diff for: src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ public static void SetPartitionKeyPropertyName(this IMutableEntityType entityTyp
196196
/// <param name="entityType">The entity type.</param>
197197
/// <returns>The names of the partition key properties, or <see langword="null"/> if not set.</returns>
198198
public static IReadOnlyList<string> GetPartitionKeyPropertyNames(this IReadOnlyEntityType entityType)
199-
=> entityType[CosmosAnnotationNames.PartitionKeyNames] as IReadOnlyList<string> ?? Array.Empty<string>();
199+
=> entityType[CosmosAnnotationNames.PartitionKeyNames] as IReadOnlyList<string>
200+
?? entityType.BaseType?.GetPartitionKeyPropertyNames()
201+
?? Array.Empty<string>();
200202

201203
/// <summary>
202204
/// Sets the names of the properties that are used to store the hierarchical partition key.

Diff for: src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs

+14
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ protected virtual void ValidateSharedContainerCompatibility(
107107
continue;
108108
}
109109

110+
if (entityType.BaseType != null
111+
&& entityType.FindAnnotation(CosmosAnnotationNames.ContainerName)?.Value != null)
112+
{
113+
throw new InvalidOperationException(
114+
CosmosStrings.ContainerNotOnRoot(entityType.DisplayName(), entityType.BaseType.DisplayName()));
115+
}
116+
110117
var ownership = entityType.FindOwnership();
111118
if (ownership != null)
112119
{
@@ -389,6 +396,13 @@ protected virtual void ValidateKeys(
389396
}
390397
else
391398
{
399+
if (entityType.BaseType != null
400+
&& entityType.FindAnnotation(CosmosAnnotationNames.PartitionKeyNames)?.Value != null)
401+
{
402+
throw new InvalidOperationException(
403+
CosmosStrings.PartitionKeyNotOnRoot(entityType.DisplayName(), entityType.BaseType.DisplayName()));
404+
}
405+
392406
foreach (var partitionKeyPropertyName in partitionKeyPropertyNames)
393407
{
394408
var partitionKey = entityType.FindProperty(partitionKeyPropertyName);

Diff for: src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ public static bool IsDocumentRoot(this IReadOnlyEntityType entityType)
2929
/// </summary>
3030
/// <param name="entityType">The entity type.</param>
3131
public static IJsonIdDefinition? GetJsonIdDefinition(this IEntityType entityType)
32-
=> entityType.GetOrAddRuntimeAnnotationValue(CosmosAnnotationNames.JsonIdDefinition,
33-
static e =>
34-
((CosmosModelRuntimeInitializerDependencies)e!.Model.FindRuntimeAnnotationValue(
35-
CosmosAnnotationNames.ModelDependencies)!).JsonIdDefinitionFactory.Create(e),
36-
entityType);
32+
=> entityType.BaseType?.GetJsonIdDefinition()
33+
?? entityType.GetOrAddRuntimeAnnotationValue(
34+
CosmosAnnotationNames.JsonIdDefinition,
35+
static e =>
36+
((CosmosModelRuntimeInitializerDependencies)e!.Model.FindRuntimeAnnotationValue(
37+
CosmosAnnotationNames.ModelDependencies)!).JsonIdDefinitionFactory.Create(e),
38+
entityType);
3739
}

Diff for: src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/EFCore.Cosmos/Properties/CosmosStrings.resx

+6
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@
132132
<data name="ContainerContainingPropertyConflict" xml:space="preserve">
133133
<value>The entity type '{entityType}' is mapped to the container '{container}' but it is also configured as being contained in property '{property}'.</value>
134134
</data>
135+
<data name="ContainerNotOnRoot" xml:space="preserve">
136+
<value>An Azure Cosmos DB container name is defined on entity type '{entityType}', which inherits from '{baseEntityType}'. Container names must be defined on the root entity type of a hierarchy.</value>
137+
</data>
135138
<data name="CosmosNotInUse" xml:space="preserve">
136139
<value>Cosmos-specific methods can only be used when the context is using the Cosmos provider.</value>
137140
</data>
@@ -292,6 +295,9 @@
292295
<data name="PartitionKeyBadValueType" xml:space="preserve">
293296
<value>The partition key value supplied for '{propertyType}' property '{entityType}.{property}' is of type '{valueType}'. Partition key values must be of a type assignable to the property.</value>
294297
</data>
298+
<data name="PartitionKeyNotOnRoot" xml:space="preserve">
299+
<value>A partition key is defined on entity type '{entityType}', which inherits from '{baseEntityType}'. Partition keys must be defined on the root entity type of a hierarchy.</value>
300+
</data>
295301
<data name="PartitionKeyMissing" xml:space="preserve">
296302
<value>Unable to execute a 'ReadItem' query since the partition key value is missing. Consider using the 'WithPartitionKey' method on the query to specify partition key to use.</value>
297303
</data>

Diff for: test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTest.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
375375
modelBuilder.Entity<SharedContainerEntity2>()
376376
.ToContainer("SharedContainer")
377377
.HasPartitionKey(e => e.PartitionKey);
378-
modelBuilder.Entity<SharedContainerEntity2Child>()
379-
.HasPartitionKey(e => e.PartitionKey);
378+
modelBuilder.Entity<SharedContainerEntity2Child>();
380379
}
381380

382381
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)

Diff for: test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ public InstantiationBinding ServiceOnlyConstructorBinding
616616
=> throw new NotImplementedException();
617617

618618
IReadOnlyEntityType IReadOnlyEntityType.BaseType
619-
=> throw new NotImplementedException();
619+
=> null!;
620620

621621
IReadOnlyModel IReadOnlyTypeBase.Model
622622
=> throw new NotImplementedException();

Diff for: test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs

+63
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,69 @@ public virtual void Detects_missing_partition_key_on_first_type()
155155
VerifyError(CosmosStrings.NoPartitionKey(nameof(Customer), "", nameof(Order), "PartitionId", "Orders"), modelBuilder);
156156
}
157157

158+
[ConditionalFact] // Issue #34176
159+
public virtual void Partition_keys_do_not_need_to_be_explicitly_configured_on_non_root_types()
160+
{
161+
var modelBuilder = CreateConventionModelBuilder();
162+
163+
modelBuilder.Entity<A>().HasPartitionKey(e => e.P0).ToContainer("As");
164+
modelBuilder.Entity<B>().HasPartitionKey(e => e.P0).ToContainer("As");
165+
modelBuilder.Entity<C>();
166+
modelBuilder.Entity<D>();
167+
modelBuilder.Entity<F>();
168+
169+
Validate(modelBuilder);
170+
}
171+
172+
[ConditionalFact] // Issue #34176
173+
public virtual void Partition_keys_can_only_be_defined_on_the_root_of_a_hierarchy()
174+
{
175+
var modelBuilder = CreateConventionModelBuilder();
176+
177+
modelBuilder.Entity<A>(
178+
b =>
179+
{
180+
b.HasPartitionKey(e => e.P0);
181+
b.ToContainer("As");
182+
b.HasKey(e => new { e.P0, e.P1 });
183+
});
184+
185+
modelBuilder.Entity<B>().HasPartitionKey(e => e.P0).ToContainer("As");
186+
modelBuilder.Entity<C>().ToContainer("As");
187+
modelBuilder.Entity<D>().HasPartitionKey(e => e.P1).ToContainer("As");
188+
modelBuilder.Entity<F>().ToContainer("As");
189+
190+
VerifyError(CosmosStrings.PartitionKeyNotOnRoot(nameof(D), nameof(A)), modelBuilder);
191+
}
192+
193+
[ConditionalFact] // Issue #34176
194+
public virtual void Container_does_not_need_to_be_explicitly_configured_on_non_root_types()
195+
{
196+
var modelBuilder = CreateConventionModelBuilder();
197+
198+
modelBuilder.Entity<A>().HasPartitionKey(e => e.P0).ToContainer("As");
199+
modelBuilder.Entity<B>().HasPartitionKey(e => e.P0).ToContainer("As");
200+
modelBuilder.Entity<C>();
201+
modelBuilder.Entity<D>();
202+
modelBuilder.Entity<F>();
203+
204+
Validate(modelBuilder);
205+
}
206+
207+
[ConditionalFact] // Issue #34176
208+
public virtual void Container_can_only_be_defined_on_the_root_of_a_hierarchy()
209+
{
210+
var modelBuilder = CreateConventionModelBuilder();
211+
212+
modelBuilder.Entity<A>().HasPartitionKey(e => e.P0).ToContainer("As");
213+
modelBuilder.Entity<B>().HasPartitionKey(e => e.P0).ToContainer("As");
214+
modelBuilder.Entity<C>();
215+
modelBuilder.Entity<D>().ToContainer("Ds");
216+
modelBuilder.Entity<F>();
217+
218+
VerifyError(CosmosStrings.ContainerNotOnRoot(nameof(D), nameof(A)), modelBuilder);
219+
}
220+
158221
[ConditionalFact]
159222
public virtual void Detects_missing_partition_keys_one_last_type()
160223
{

0 commit comments

Comments
 (0)