Skip to content

Commit d48457e

Browse files
authored
Fix saving null complex properties with nested required complex properties (#36472)
Change the convention to automatically configure nested properties inside complex types as complex properties instead of navigations Throw for shadow properties on value complex types Throw for complex collections not mapped to JSON Warn when mapping a collection as non-collection complex property Fix typos in string resources Part of #31237
1 parent 50fc45d commit d48457e

File tree

39 files changed

+555
-372
lines changed

39 files changed

+555
-372
lines changed

src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1547,7 +1547,9 @@ private void GenerateAddMapping(
15471547
}
15481548

15491549
var table = tableMapping.Table;
1550-
var isOptional = table.IsOptional(typeBase);
1550+
var isOptional = typeBase.IsMappedToJson()
1551+
? (bool?)null
1552+
: table.IsOptional(typeBase);
15511553
mainBuilder
15521554
.AppendLine($"{tableVariable}.AddTypeMapping({tableMappingVariable}, {code.Literal(isOptional)});")
15531555
.AppendLine($"{tableMappingsVariable}.Add({tableMappingVariable});");

src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,18 @@ static void ValidateType(ITypeBase typeBase)
9898
}
9999

100100
/// <inheritdoc/>
101-
protected override void ValidatePropertyMapping(IConventionComplexProperty complexProperty)
101+
protected override void ValidatePropertyMapping(
102+
IConventionComplexProperty complexProperty,
103+
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
102104
{
103-
base.ValidatePropertyMapping(complexProperty);
105+
base.ValidatePropertyMapping(complexProperty, logger);
106+
107+
if (complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson())
108+
{
109+
throw new InvalidOperationException(
110+
RelationalStrings.ComplexCollectionNotMappedToJson(
111+
complexProperty.DeclaringType.DisplayName(), complexProperty.Name));
112+
}
104113

105114
if (!complexProperty.ComplexType.IsMappedToJson()
106115
&& complexProperty.IsNullable

src/EFCore.Relational/Metadata/Internal/TableBase.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,13 @@ public override bool IsReadOnly
126126
/// any release. You should only use it directly in your code with extreme caution and knowing that
127127
/// doing so can result in application failures when updating to a new Entity Framework Core release.
128128
/// </summary>
129-
public virtual void AddTypeMapping(ITableMappingBase tableMapping, bool optional)
129+
public virtual void AddTypeMapping(ITableMappingBase tableMapping, bool? optional)
130130
{
131-
OptionalTypes ??= new Dictionary<ITypeBase, bool>();
132-
133-
OptionalTypes.Add(tableMapping.TypeBase, optional);
131+
if (optional.HasValue)
132+
{
133+
OptionalTypes ??= [];
134+
OptionalTypes.Add(tableMapping.TypeBase, optional.Value);
135+
}
134136

135137
if (tableMapping.TypeBase is IEntityType)
136138
{

src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Lines changed: 30 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EFCore.Relational/Properties/RelationalStrings.resx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@
148148
<data name="CompiledModelFunctionTranslation" xml:space="preserve">
149149
<value>The function '{function}' has a custom translation. Compiled model can't be generated, because custom function translations are not supported.</value>
150150
</data>
151+
<data name="ComplexCollectionNotMappedToJson" xml:space="preserve">
152+
<value>The complex collection property '{entityType}.{property}' must be mapped to a JSON column. Use 'ToJson()' to configure this complex collection as mapped to a JSON column.</value>
153+
</data>
151154
<data name="ComplexPropertyBothJsonColumnAndJsonPropertyName" xml:space="preserve">
152155
<value>Complex property '{complexProperty}' cannot have both a JSON column name ('{columnName}') and a JSON property name ('{propertyName}') configured. Use ToJson() to map to a JSON column or HasJsonPropertyName() to map as a JSON property within a containing JSON column, but not both.</value>
153156
</data>
@@ -157,9 +160,6 @@
157160
<data name="ComplexPropertyJsonPropertyNameWithoutJsonMapping" xml:space="preserve">
158161
<value>Complex property '{complexProperty}' cannot use 'HasJsonPropertyName()' because it is not contained within a JSON-mapped type. Use 'ToJson()' to map the complex property to a JSON column, or ensure it is contained within a type that is mapped to JSON.</value>
159162
</data>
160-
<data name="PropertyBothColumnNameAndJsonPropertyName" xml:space="preserve">
161-
<value>Property '{property}' cannot have both a column name ('{columnName}') and a JSON property name ('{jsonPropertyName}') configured. Properties in JSON-mapped types should use JSON property names, not column names.</value>
162-
</data>
163163
<data name="ComplexPropertyOptionalTableSharing" xml:space="preserve">
164164
<value>The optional complex property '{type}.{property}' is mapped to columns by flattening the contained properties, but it only contains optional properties. Add a required property or discriminator or map this complex property to a JSON column.</value>
165165
</data>
@@ -439,12 +439,12 @@
439439
<data name="ExecuteUpdateDeleteOnEntityNotMappedToTable" xml:space="preserve">
440440
<value>ExecuteUpdate or ExecuteDelete was called on entity type '{entityType}', but that entity type is not mapped to a table.</value>
441441
</data>
442-
<data name="ExecuteUpdateSubqueryNotSupportedOverComplexTypes" xml:space="preserve">
443-
<value>ExecuteUpdate is being used over a LINQ operator which isn't natively supported by the database; this cannot be translated because complex type '{complexType}' is projected out. Rewrite your query to project out the containing entity type instead.</value>
444-
</data>
445442
<data name="ExecuteUpdateOverJsonIsNotSupported" xml:space="preserve">
446443
<value>ExecuteUpdate is being used over type '{structuralType}' which is mapped to JSON; ExecuteUpdate on JSON is not supported.</value>
447444
</data>
445+
<data name="ExecuteUpdateSubqueryNotSupportedOverComplexTypes" xml:space="preserve">
446+
<value>ExecuteUpdate is being used over a LINQ operator which isn't natively supported by the database; this cannot be translated because complex type '{complexType}' is projected out. Rewrite your query to project out the containing entity type instead.</value>
447+
</data>
448448
<data name="ExplicitDefaultConstraintNamesNotSupportedForTpc" xml:space="preserve">
449449
<value>Can't use explicitly named default constraints with TPC inheritance or entity splitting. Constraint name: '{explicitDefaultConstraintName}'.</value>
450450
</data>
@@ -806,7 +806,7 @@
806806
<comment>Warning RelationalEventId.ModelValidationKeyDefaultValueWarning string string</comment>
807807
</data>
808808
<data name="LogKeyPropertiesNotMappedToTable" xml:space="preserve">
809-
<value>The key {keyProperties} on the entity type '{entityType}' cannot be represented in the database. Either all or some of the properties aren't mapped to table '{table}'. All key properties must be mapped to a single table for the unique constraint to be created.</value>
809+
<value>The key {keyProperties} on the entity type '{entityType}' cannot be represented in the database. Some or all of the properties aren't mapped to table '{table}'. All key properties must be mapped to a single table for the unique constraint to be created.</value>
810810
<comment>Error RelationalEventId.KeyPropertiesNotMappedToTable string string string</comment>
811811
</data>
812812
<data name="LogMigrating" xml:space="preserve">
@@ -962,7 +962,7 @@
962962
<value>Using '{methodName}' on DbSet of '{entityType}' is not supported since '{entityType}' is part of hierarchy and does not contain a discriminator property.</value>
963963
</data>
964964
<data name="MigrationDownMissing" xml:space="preserve">
965-
<value>The 'Down' method for this migration has not been implemented. Both the 'Up' abd 'Down' methods must be implemented to support reverting migrations.</value>
965+
<value>The 'Down' method for this migration has not been implemented. Both the 'Up' and 'Down' methods must be implemented to support reverting migrations.</value>
966966
</data>
967967
<data name="MigrationNotFound" xml:space="preserve">
968968
<value>The migration '{migrationName}' was not found.</value>
@@ -980,7 +980,7 @@
980980
<value>No value was provided for the required parameter '{parameter}'.</value>
981981
</data>
982982
<data name="MissingResultSetWhenSaving" xml:space="preserve">
983-
<value>A result set was was missing when reading the results of a SaveChanges operation; this may indicate that a stored procedure was configured to return results in the EF model, but did not. Check your stored procedure definitions.</value>
983+
<value>A result set was missing when reading the results of a SaveChanges operation; this may indicate that a stored procedure was configured to return results in the EF model, but did not. Check your stored procedure definitions.</value>
984984
</data>
985985
<data name="ModificationCommandBatchAlreadyComplete" xml:space="preserve">
986986
<value>Cannot add commands to a completed ModificationCommandBatch.</value>
@@ -1078,6 +1078,9 @@
10781078
<data name="ProjectionMappingCountMismatch" xml:space="preserve">
10791079
<value>Unable to translate set operations when both sides don't assign values to the same properties in the nominal type. Please make sure that the same properties are included on both sides, and consider assigning default values if a property doesn't require a specific value.</value>
10801080
</data>
1081+
<data name="PropertyBothColumnNameAndJsonPropertyName" xml:space="preserve">
1082+
<value>Property '{property}' cannot have both a column name ('{columnName}') and a JSON property name ('{jsonPropertyName}') configured. Properties in JSON-mapped types should use JSON property names, not column names.</value>
1083+
</data>
10811084
<data name="PropertyNotMapped" xml:space="preserve">
10821085
<value>The '{propertyType}' property '{entityType}.{property}' could not be mapped to the database type '{storeType}' because the database provider does not support mapping '{propertyType}' properties to '{storeType}' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.</value>
10831086
</data>
@@ -1223,7 +1226,7 @@
12231226
<value>The query requires a subquery over complex type '{complexType}'. Subqueries over complex types are currently unsupported.</value>
12241227
</data>
12251228
<data name="TableNotMappedEntityType" xml:space="preserve">
1226-
<value>The entity type '{entityType}' is not mapped to the store object '{table}'.</value>
1229+
<value>The type '{entityType}' is not mapped to the store object '{table}'.</value>
12271230
</data>
12281231
<data name="TableOverrideMismatch" xml:space="preserve">
12291232
<value>The property '{propertySpecification}' has specific configuration for the table '{table}', but isn't mapped to a column on that table. Remove the specific configuration, or map an entity type that contains this property to '{table}'.</value>

0 commit comments

Comments
 (0)