Skip to content

Commit 4ee7d30

Browse files
authored
Add support for complex type discriminator. (#35744)
Part of #31376
1 parent 73d6864 commit 4ee7d30

File tree

66 files changed

+2106
-761
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2106
-761
lines changed

Diff for: src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ private static void ProcessEntityType(IConventionEntityTypeBuilder entityTypeBui
9797

9898
/// <inheritdoc />
9999
public override void ProcessDiscriminatorPropertySet(
100-
IConventionEntityTypeBuilder entityTypeBuilder,
100+
IConventionTypeBaseBuilder structuralTypeBuilder,
101101
string? name,
102102
IConventionContext<string> context)
103103
{
104-
var entityType = entityTypeBuilder.Metadata;
105-
if (entityType.IsDocumentRoot())
104+
if (structuralTypeBuilder.Metadata is not IConventionEntityType entityType
105+
|| entityType.IsDocumentRoot())
106106
{
107-
base.ProcessDiscriminatorPropertySet(entityTypeBuilder, name, context);
107+
base.ProcessDiscriminatorPropertySet(structuralTypeBuilder, name, context);
108108
}
109109
}
110110

Diff for: src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs

+10-11
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,10 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
174174
}
175175

176176
// Don't chain, because each of these could return null if the property has been explicitly configured with some other value.
177-
computedIdPropertyBuilder = computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName)
178-
?? computedIdPropertyBuilder;
179-
180-
computedIdPropertyBuilder = computedIdPropertyBuilder.IsRequired(true)
181-
?? computedIdPropertyBuilder;
182-
183-
computedIdPropertyBuilder = computedIdPropertyBuilder.HasValueGeneratorFactory(typeof(IdValueGeneratorFactory))
184-
?? computedIdPropertyBuilder;
185-
177+
computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName);
178+
computedIdPropertyBuilder.HasValueGeneratorFactory(typeof(IdValueGeneratorFactory));
186179
computedIdPropertyBuilder.AfterSave(PropertySaveBehavior.Throw);
180+
computedIdPropertyBuilder.IsRequired(true);
187181
}
188182

189183
/// <inheritdoc />
@@ -327,8 +321,13 @@ public virtual void ProcessModelAnnotationChanged(
327321

328322
/// <inheritdoc />
329323
public virtual void ProcessDiscriminatorPropertySet(
330-
IConventionEntityTypeBuilder entityTypeBuilder,
324+
IConventionTypeBaseBuilder structuralTypeBuilder,
331325
string? name,
332326
IConventionContext<string?> context)
333-
=> ProcessEntityType(entityTypeBuilder.Metadata, context);
327+
{
328+
if (structuralTypeBuilder is IConventionEntityTypeBuilder entityTypeBuilder)
329+
{
330+
ProcessEntityType(entityTypeBuilder.Metadata, context);
331+
}
332+
}
334333
}

Diff for: src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs

+53-2
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,60 @@ protected virtual void GenerateComplexPropertyAnnotations(
628628
IComplexProperty property,
629629
IndentedStringBuilder stringBuilder)
630630
{
631+
var discriminatorProperty = property.ComplexType.FindDiscriminatorProperty();
632+
if (discriminatorProperty != null)
633+
{
634+
stringBuilder
635+
.AppendLine()
636+
.Append(propertyBuilderName)
637+
.Append('.')
638+
.Append("HasDiscriminator");
639+
640+
if (discriminatorProperty.DeclaringType == property.ComplexType
641+
&& discriminatorProperty.Name != "Discriminator")
642+
{
643+
var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType
644+
.MakeNullable(discriminatorProperty.IsNullable)
645+
?? discriminatorProperty.ClrType;
646+
stringBuilder
647+
.Append('<')
648+
.Append(Code.Reference(propertyClrType))
649+
.Append(">(")
650+
.Append(Code.Literal(discriminatorProperty.Name))
651+
.Append(')');
652+
}
653+
else
654+
{
655+
stringBuilder
656+
.Append("()");
657+
}
658+
659+
var discriminatorValue = property.ComplexType.GetDiscriminatorValue();
660+
if (discriminatorValue != null)
661+
{
662+
if (discriminatorProperty != null)
663+
{
664+
var valueConverter = FindValueConverter(discriminatorProperty);
665+
if (valueConverter != null)
666+
{
667+
discriminatorValue = valueConverter.ConvertToProvider(discriminatorValue);
668+
}
669+
}
670+
671+
stringBuilder
672+
.Append('.')
673+
.Append("HasValue")
674+
.Append('(')
675+
.Append(Code.UnknownLiteral(discriminatorValue))
676+
.Append(')');
677+
}
678+
679+
stringBuilder.AppendLine(";");
680+
}
681+
631682
var propertyAnnotations = Dependencies.AnnotationCodeGenerator
632-
.FilterIgnoredAnnotations(property.GetAnnotations())
633-
.ToDictionary(a => a.Name, a => a);
683+
.FilterIgnoredAnnotations(property.GetAnnotations())
684+
.ToDictionary(a => a.Name, a => a);
634685

635686
var typeAnnotations = Dependencies.AnnotationCodeGenerator
636687
.FilterIgnoredAnnotations(property.ComplexType.GetAnnotations())

Diff for: src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs

+26-8
Original file line numberDiff line numberDiff line change
@@ -925,14 +925,6 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator
925925
.Append(_code.Literal(entityType.HasSharedClrType));
926926
}
927927

928-
var discriminatorProperty = entityType.GetDiscriminatorPropertyName();
929-
if (discriminatorProperty != null)
930-
{
931-
mainBuilder.AppendLine(",")
932-
.Append("discriminatorProperty: ")
933-
.Append(_code.Literal(discriminatorProperty));
934-
}
935-
936928
var changeTrackingStrategy = entityType.GetChangeTrackingStrategy();
937929
if (changeTrackingStrategy != ChangeTrackingStrategy.Snapshot)
938930
{
@@ -959,6 +951,14 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator
959951
.Append(_code.Literal(true));
960952
}
961953

954+
var discriminatorProperty = entityType.GetDiscriminatorPropertyName();
955+
if (discriminatorProperty != null)
956+
{
957+
mainBuilder.AppendLine(",")
958+
.Append("discriminatorProperty: ")
959+
.Append(_code.Literal(discriminatorProperty));
960+
}
961+
962962
var discriminatorValue = entityType.GetDiscriminatorValue();
963963
if (discriminatorValue != null)
964964
{
@@ -2182,6 +2182,24 @@ private void CreateComplexProperty(
21822182
.Append(_code.Literal(true));
21832183
}
21842184

2185+
var discriminatorPropertyName = complexType.GetDiscriminatorPropertyName();
2186+
if (discriminatorPropertyName != null)
2187+
{
2188+
mainBuilder.AppendLine(",")
2189+
.Append("discriminatorProperty: ")
2190+
.Append(_code.Literal(discriminatorPropertyName));
2191+
}
2192+
2193+
var discriminatorValue = complexType.GetDiscriminatorValue();
2194+
if (discriminatorValue != null)
2195+
{
2196+
AddNamespace(discriminatorValue.GetType(), parameters.Namespaces);
2197+
2198+
mainBuilder.AppendLine(",")
2199+
.Append("discriminatorValue: ")
2200+
.Append(_code.UnknownLiteral(discriminatorValue));
2201+
}
2202+
21852203
mainBuilder.AppendLine(",")
21862204
.Append("propertyCount: ")
21872205
.Append(_code.Literal(complexType.GetDeclaredProperties().Count()));

Diff for: src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs

+5
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,11 @@ protected override void ValidateInheritanceMapping(
19571957
var discriminatorValues = new Dictionary<string, IEntityType>();
19581958
foreach (var derivedType in derivedTypes)
19591959
{
1960+
foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
1961+
{
1962+
ValidateDiscriminatorValues(complexProperty.ComplexType);
1963+
}
1964+
19601965
var discriminatorValue = derivedType.GetDiscriminatorValue();
19611966
if (!derivedType.ClrType.IsInstantiable()
19621967
|| discriminatorValue is null)

Diff for: src/EFCore.Relational/Metadata/Conventions/DiscriminatorLengthConvention.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder,
5454
&& !discriminatorProperty.IsForeignKey())
5555
{
5656
var maxDiscriminatorValueLength =
57-
entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();
57+
entityType.GetDerivedTypesInclusive().Select(e => (e.GetDiscriminatorValue() as string)?.Length ?? 0).Max();
5858

5959
var previous = 1;
6060
var current = 1;

Diff for: src/EFCore/Infrastructure/ModelValidator.cs

+68-2
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,16 @@ protected virtual void ValidateInheritanceMapping(
640640
/// <param name="rootEntityType">The entity type to validate.</param>
641641
protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType)
642642
{
643-
var derivedTypes = rootEntityType.GetDerivedTypesInclusive().ToList();
643+
var derivedTypes = rootEntityType.GetDerivedTypesInclusive();
644644
var discriminatorProperty = rootEntityType.FindDiscriminatorProperty();
645645
if (discriminatorProperty == null)
646646
{
647-
if (derivedTypes.Count == 1)
647+
if (!derivedTypes.Skip(1).Any())
648648
{
649+
foreach (var complexProperty in rootEntityType.GetDeclaredComplexProperties())
650+
{
651+
ValidateDiscriminatorValues(complexProperty.ComplexType);
652+
}
649653
return;
650654
}
651655

@@ -654,6 +658,68 @@ protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType)
654658
}
655659

656660
var discriminatorValues = new Dictionary<object, IEntityType>(discriminatorProperty.GetKeyValueComparer());
661+
foreach (var derivedType in derivedTypes)
662+
{
663+
foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
664+
{
665+
ValidateDiscriminatorValues(complexProperty.ComplexType);
666+
}
667+
668+
if (!derivedType.ClrType.IsInstantiable())
669+
{
670+
continue;
671+
}
672+
673+
var discriminatorValue = derivedType[CoreAnnotationNames.DiscriminatorValue];
674+
if (discriminatorValue == null)
675+
{
676+
throw new InvalidOperationException(
677+
CoreStrings.NoDiscriminatorValue(derivedType.DisplayName()));
678+
}
679+
680+
if (!discriminatorProperty.ClrType.IsInstanceOfType(discriminatorValue))
681+
{
682+
throw new InvalidOperationException(
683+
CoreStrings.DiscriminatorValueIncompatible(
684+
discriminatorValue, derivedType.DisplayName(), discriminatorProperty.ClrType.DisplayName()));
685+
}
686+
687+
if (discriminatorValues.TryGetValue(discriminatorValue, out var duplicateEntityType))
688+
{
689+
throw new InvalidOperationException(
690+
CoreStrings.DuplicateDiscriminatorValue(
691+
derivedType.DisplayName(), discriminatorValue, duplicateEntityType.DisplayName()));
692+
}
693+
694+
discriminatorValues[discriminatorValue] = derivedType;
695+
}
696+
}
697+
698+
/// <summary>
699+
/// Validates the discriminator and values for the given complex type and nested ones.
700+
/// </summary>
701+
/// <param name="complexType">The entity type to validate.</param>
702+
protected virtual void ValidateDiscriminatorValues(IComplexType complexType)
703+
{
704+
foreach (var complexProperty in complexType.GetComplexProperties())
705+
{
706+
ValidateDiscriminatorValues(complexProperty.ComplexType);
707+
}
708+
709+
var derivedTypes = complexType.GetDerivedTypesInclusive();
710+
var discriminatorProperty = complexType.FindDiscriminatorProperty();
711+
if (discriminatorProperty == null)
712+
{
713+
if (!derivedTypes.Skip(1).Any())
714+
{
715+
return;
716+
}
717+
718+
throw new InvalidOperationException(
719+
CoreStrings.NoDiscriminatorProperty(complexType.DisplayName()));
720+
}
721+
722+
var discriminatorValues = new Dictionary<object, IComplexType>(discriminatorProperty.GetKeyValueComparer());
657723

658724
foreach (var derivedType in derivedTypes)
659725
{

0 commit comments

Comments
 (0)