-
Notifications
You must be signed in to change notification settings - Fork 2k
Document breaking changes for complex types in EF Core 10.0 #5113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
ab769de
76f1be5
829de18
71791b6
e6cca70
94791e4
1d1a23e
c98638c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,10 @@ This page documents API and behavior changes that have the potential to break ex | |
|:--------------------------------------------------------------------------------------------------------------- | -----------| | ||
| [SQL Server json data type used by default on Azure SQL and compatibility level 170](#sqlserver-json-data-type) | Low | | ||
| [ExecuteUpdateAsync now accepts a regular, non-expression lambda](#ExecuteUpdateAsync-lambda) | Low | | ||
| [Compiled models now throw exception for value converters with private methods](#compiled-model-private-methods) | Low | | ||
|
||
| [Complex type column names are now uniquified](#complex-type-column-uniquification) | Low | | ||
| [IDiscriminatorPropertySetConvention signature changed](#discriminator-convention-signature) | Low | | ||
| [Nested complex type properties use full path in column names](#nested-complex-type-column-names) | Low | | ||
|
||
## Low-impact changes | ||
|
||
|
@@ -178,6 +182,170 @@ await context.Blogs.ExecuteUpdateAsync(s => | |
}); | ||
``` | ||
|
||
<a name="compiled-model-private-methods"></a> | ||
|
||
### Compiled models now throw exception for value converters with private methods | ||
|
||
[Tracking Issue #35033](https://github.com/dotnet/efcore/issues/35033) | ||
|
||
#### Old behavior | ||
|
||
Previously, when using value converters with compiled models (using `dotnet ef dbcontext optimize`), EF would reference the converter type and everything worked correctly. | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
```c# | ||
public sealed class BooleanToCharConverter : ValueConverter<bool, char> | ||
{ | ||
public static readonly BooleanToCharConverter Default = new(); | ||
|
||
public BooleanToCharConverter() | ||
|
||
: base(v => ConvertToChar(v), v => ConvertToBoolean(v)) | ||
{ | ||
} | ||
|
||
private static char ConvertToChar(bool value) // Private method | ||
{ | ||
return value ? 'Y' : 'N'; | ||
} | ||
|
||
private static bool ConvertToBoolean(char value) // Private method | ||
{ | ||
return value == 'Y'; | ||
} | ||
} | ||
``` | ||
|
||
Running `dotnet ef dbcontext optimize` would generate code that attempted to reference these private methods, causing CS0122 compilation errors. | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### New behavior | ||
|
||
Starting with EF Core 10.0, EF generates code that directly references the conversion methods themselves. If these methods are private, compilation will fail. | ||
|
||
#### Why | ||
|
||
This change improves performance by generating more direct code, but requires that conversion methods be accessible to the generated code. | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### Mitigations | ||
|
||
Make the methods referenced by value converters public or internal instead of private: | ||
|
||
```c# | ||
AndriySvyryd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public sealed class BooleanToCharConverter : ValueConverter<bool, char> | ||
{ | ||
public static readonly BooleanToCharConverter Default = new(); | ||
|
||
public BooleanToCharConverter() | ||
: base(v => ConvertToChar(v), v => ConvertToBoolean(v)) | ||
{ | ||
} | ||
|
||
public static char ConvertToChar(bool value) // Now public | ||
{ | ||
return value ? 'Y' : 'N'; | ||
} | ||
|
||
public static bool ConvertToBoolean(char value) // Now public | ||
{ | ||
return value == 'Y'; | ||
} | ||
} | ||
``` | ||
|
||
<a name="complex-type-column-uniquification"></a> | ||
|
||
### Complex type column names are now uniquified | ||
|
||
[Tracking Issue #4970](https://github.com/dotnet/EntityFramework.Docs/issues/4970) | ||
AndriySvyryd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Old behavior | ||
|
||
Previously, when mapping complex types to table columns, if multiple complex type properties had the same column name, they would silently share the same column. | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### New behavior | ||
|
||
Starting with EF Core 10.0, complex type column names are uniquified by appending a number at the end if another column with the same name exists on the table. | ||
|
||
#### Why | ||
|
||
This prevents data corruption that could occur when multiple properties unintentionally mapped to the same column. | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### Mitigations | ||
|
||
If you need specific column names, configure them explicitly: | ||
|
||
```c# | ||
modelBuilder.Entity<Customer>(b => | ||
{ | ||
b.ComplexProperty(c => c.ShippingAddress, p => p.Property(a => a.Street).HasColumnName("ShippingStreet")); | ||
b.ComplexProperty(c => c.BillingAddress, p => p.Property(a => a.Street).HasColumnName("BillingStreet")); | ||
}); | ||
``` | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
<a name="discriminator-convention-signature"></a> | ||
|
||
### IDiscriminatorPropertySetConvention signature changed | ||
AndriySvyryd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
[Tracking Issue #4970](https://github.com/dotnet/EntityFramework.Docs/issues/4970) | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### Old behavior | ||
|
||
Previously, `IDiscriminatorPropertySetConvention.ProcessDiscriminatorPropertySet` took `IConventionEntityTypeBuilder` as a parameter. | ||
|
||
#### New behavior | ||
|
||
Starting with EF Core 10.0, the method signature changed to take `IConventionTypeBaseBuilder` instead of `IConventionEntityTypeBuilder`. | ||
|
||
#### Why | ||
|
||
This change allows the convention to work with both entity types and complex types. | ||
|
||
#### Mitigations | ||
|
||
Update your custom convention implementations to use the new signature: | ||
|
||
```c# | ||
public virtual void ProcessDiscriminatorPropertySet( | ||
IConventionTypeBaseBuilder typeBaseBuilder, // Changed from IConventionEntityTypeBuilder | ||
string name, | ||
Type type, | ||
MemberInfo memberInfo, | ||
IConventionContext<IConventionProperty> context) | ||
``` | ||
|
||
<a name="nested-complex-type-column-names"></a> | ||
|
||
### Nested complex type properties use full path in column names | ||
|
||
[Tracking Issue #4947](https://github.com/dotnet/EntityFramework.Docs/issues/4947) | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
#### Old behavior | ||
|
||
Previously, properties on nested complex types were mapped to columns using just the declaring type name. For example, `EntityType.Owned.Complex.Property` was mapped to column `Complex_Property`. | ||
|
||
#### New behavior | ||
|
||
Starting with EF Core 10.0, properties on nested complex types use the full path to the property as part of the column name. For example, `EntityType.Owned.Complex.Property` is now mapped to column `Owned_Complex_Property`. | ||
|
||
#### Why | ||
|
||
This provides better column name uniqueness and makes it clearer which property maps to which column. | ||
|
||
#### Mitigations | ||
|
||
If you need to maintain the old column names, configure them explicitly: | ||
|
||
```c# | ||
modelBuilder.Entity<EntityType>(b => | ||
{ | ||
b.ComplexProperty(e => e.Owned, owned => | ||
{ | ||
owned.ComplexProperty(o => o.Complex, complex => | ||
{ | ||
complex.Property(c => c.Property).HasColumnName("Complex_Property"); | ||
}); | ||
}); | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
}); | ||
``` | ||
|
||
<a name="MDS-breaking-changes"></a> | ||
|
||
## Microsoft.Data.Sqlite breaking changes | ||
|
Uh oh!
There was an error while loading. Please reload this page.