Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ Compiled models have some limitations:

* [Global query filters are not supported](https://github.com/dotnet/efcore/issues/24897).
* [Lazy loading and change-tracking proxies are not supported](https://github.com/dotnet/efcore/issues/24902).
* Value converters that reference private methods are not supported. Make referenced methods public or internal instead.
* [The model must be manually synchronized by regenerating it any time the model definition or configuration change](https://github.com/dotnet/efcore/issues/24894).
* Custom IModelCacheKeyFactory implementations are not supported. However, you can compile multiple models and load the appropriate one as needed.

Expand Down
90 changes: 90 additions & 0 deletions entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ 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 |
| [Complex type column names are now uniquified](#complex-type-column-uniquification) | Low |
| [Nested complex type properties use full path in column names](#nested-complex-type-column-names) | Low |
| [IDiscriminatorPropertySetConvention signature changed](#discriminator-convention-signature) | Low |

## Low-impact changes

Expand Down Expand Up @@ -178,6 +181,93 @@ await context.Blogs.ExecuteUpdateAsync(s =>
});
```

<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)

#### Old behavior

Previously, when mapping complex types to table columns, if multiple properties in different complex types had the same column name, they would silently share the same column.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this relate to the breaking change just below - are they dups? In other words, if the full path is now used (the change below), how is it possible to for multiple properties in different complex types to have the same column name (i.e. when would appending the number be necessary)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's less likely, but still possible if the user explicitly set the column name on one that matches the default column name of the other.


#### 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 are unintentionally mapped to the same column.

#### Mitigations

If you need multiple properties to share the same column, configure them explicitly:

```c#
modelBuilder.Entity<Customer>(b =>
{
b.ComplexProperty(c => c.ShippingAddress, p => p.Property(a => a.Street).HasColumnName("Street"));
b.ComplexProperty(c => c.BillingAddress, p => p.Property(a => a.Street).HasColumnName("Street"));
});
```

<a name="nested-complex-type-column-names"></a>

### Nested complex type properties use full path in column names

#### Old behavior

Previously, properties on nested complex types were mapped to columns using just the declaring type name. For example, `EntityType.Complex.NestedComplex.Property` was mapped to column `NestedComplex_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.Complex.NestedComplex.Property` is now mapped to column `Complex_NestedComplex_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>()
.ComplexProperty(e => e.Complex)
.ComplexProperty(o => o.NestedComplex)
.Property(c => c.Property)
.HasColumnName("NestedComplex_Property");
```

<a name="discriminator-convention-signature"></a>

### IDiscriminatorPropertySetConvention signature changed

#### 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="MDS-breaking-changes"></a>

## Microsoft.Data.Sqlite breaking changes
Expand Down
36 changes: 36 additions & 0 deletions entity-framework/core/what-is-new/ef-core-9.0/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ EF Core 9 targets .NET 8. This means that existing applications that target .NET
| [Exception is thrown when applying migrations in an explicit transaction](#migrations-transaction) | High |
| [`Microsoft.EntityFrameworkCore.Design` not found when using EF tools](#tools-design) | Medium |
| [`EF.Functions.Unhex()` now returns `byte[]?`](#unhex) | Low |
| [Compiled models now reference value converter methods directly](#compiled-model-private-methods) | Low |
| [SqlFunctionExpression's nullability arguments' arity validated](#sqlfunctionexpression-nullability) | Low |
| [`ToString()` method now returns empty string for `null` instances](#nullable-tostring) | Low |
| [Shared framework dependencies were updated to 9.0.x](#shared-framework-dependencies) | Low |
Expand Down Expand Up @@ -228,6 +229,41 @@ var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)

Otherwise, add runtime checks for null on the return value of Unhex().

<a name="compiled-model-private-methods"></a>

### Compiled models now reference value converter methods directly

[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.

```c#
public sealed class BooleanToCharConverter() : ValueConverter<bool, char>(v => ConvertToChar(v), v => ConvertToBoolean(v))
{
public static readonly BooleanToCharConverter Default = new();

private static char ConvertToChar(bool value) // Private method
=> value ? 'Y' : 'N';

private static bool ConvertToBoolean(char value) // Private method
=> value == 'Y';
}
```

#### New behavior

Starting with EF Core 9.0, EF generates code that directly references the conversion methods themselves. If these methods are private, compilation will fail.

#### Why

This change was necessary to support NativeAOT.

#### Mitigations

Make the methods referenced by value converters public or internal instead of private.

<a name="sqlfunctionexpression-nullability"></a>

### SqlFunctionExpression's nullability arguments' arity validated
Expand Down