Skip to content

Commit 8ba78fe

Browse files
committed
Document 8.0 breaking changes
Fixes #4518 Fixes #4517 Fixes #4516 Fixes #4506 Fixes #4469 Part of #4412
1 parent cb01aef commit 8ba78fe

File tree

1 file changed

+198
-5
lines changed

1 file changed

+198
-5
lines changed

entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md

+198-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ This page documents API and behavior changes that have the potential to break ex
1212

1313
## Summary
1414

15-
| **Breaking change** | **Impact** |
16-
|:---------------------------------------------------------------------------------------------------------|------------|
17-
| [`Contains` in LINQ queries may stop working on older SQL Server versions | High |
18-
| [SQL Server `date` and `time` now scaffold to .NET `DateOnly` and `TimeOnly`](#sqlserver-date-time-only) | Medium |
19-
| [SQLite `Math` methods now translate to SQL](#sqlite-math) | Low |
15+
| **Breaking change** | **Impact** |
16+
|:--------------------------------------------------------------------------------------------------------------|------------|
17+
| [`Contains` in LINQ queries may stop working on older SQL Server versions](#sqlserver-contains-compatibility) | High |
18+
| [Enums in JSON are stored as ints instead of strings by default](#enums-as-ints) | High |
19+
| [SQL Server `date` and `time` now scaffold to .NET `DateOnly` and `TimeOnly`](#sqlserver-date-time-only) | Medium |
20+
| [Boolean columns with a database generated value are no longer scaffolded as nullable](#scaffold-bools) | Medium |
21+
| [SQLite `Math` methods now translate to SQL](#sqlite-math) | Low |
22+
| [ITypeBase replaces IEntityType in some APIs](#type-base) | Low |
23+
| [ValueGenerator expressions must use public APIs](#value-converters) | Low |
24+
| [ExcludeFromMigrations no longer excludes other tables in a TPC hierarchy](#exclude-from-migrations) | Low |
25+
| [Non-shadow integer keys are persisted to Cosmos documents](#persist-to-cosmos) | Low |
2026

2127
## High-impact changes
2228

@@ -78,6 +84,32 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
7884
.UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));
7985
```
8086

87+
<a name="enums-as-ints"></a>
88+
89+
### Enums in JSON are stored as ints instead of strings by default
90+
91+
[Tracking Issue #13617](https://github.com/dotnet/efcore/issues/31100)
92+
93+
#### Old behavior
94+
95+
In EF7, [enums mapped to JSON](xref:core/what-is-new/ef-core-7.0/whatsnew#json-columns) are, by default, stored as string values in the JSON document.
96+
97+
#### New behavior
98+
99+
Starting with EF Core 8.0, EF now, by default, maps enums to integer values in the JSON document.
100+
101+
#### Why
102+
103+
EF has always, by default, mapped enums to a numeric column in relational databases. Since EF supports queries where values from JSON interact with values from columns and parameters, it is important that the values in JSON match the values in the non-JSON column.
104+
105+
#### Mitigations
106+
107+
To continue using strings, configure the enum property with a conversion. For example:
108+
109+
```csharp
110+
modelBuilder.Entity<User>().Property(e => e.Status).HasConversion<string>();
111+
```
112+
81113
## Medium-impact changes
82114

83115
<a name="sqlserver-date-time-only"></a>
@@ -123,6 +155,42 @@ It is recommended to react to this change by modifying your code to use the newl
123155
<#
124156
```
125157

158+
<a name="scaffold-bools"></a>
159+
160+
### Boolean columns with a database generated value are no longer scaffolded as nullable
161+
162+
[Tracking Issue #15070](https://github.com/dotnet/efcore/issues/15070)
163+
164+
#### Old behavior
165+
166+
Previously, non-nullable `bool` columns with a database default constraint were scaffolded as nullable `bool?` properties.
167+
168+
#### New behavior
169+
170+
Starting with EF Core 8.0, non-nullable `bool` columns are always scaffolded as non-nullable properties.
171+
172+
#### Why
173+
174+
A `bool` property will not have its value sent to the database if that value is `false`, which is the CLR default. If the database has a default value of `true` for the column, then even though the value of the property is `false`, the value in the database ends up as `true`. However, in EF8, the sentinel used to determine if a property has a value or not can be changed. This is done automatically for `bool` properties with a database generated value of `true`, which means that it is no longer necessary to scaffold the properties as nullable.
175+
176+
#### Mitigations
177+
178+
This change only affects users which regularly re-scaffold their database into an EF code model ("database-first" flow).
179+
180+
It is recommended to react to this change by modifying your code to use the non-nullable bool property. However, if that isn't possible, you can edit the scaffolding templates to revert to the previous mapping. To do this, set up the templates as described on [this page](xref:core/managing-schemas/scaffolding/templates). Then, edit the `EntityType.t4` file, find where the entity properties get generated (search for `property.ClrType`), and change the code to the following:
181+
182+
```c#
183+
#>
184+
var propertyClrType = property.ClrType != typeof(bool)
185+
|| (property.GetDefaultValueSql() == null && property.GetDefaultValue() != null)
186+
? property.ClrType
187+
: typeof(bool?);
188+
#>
189+
public <#= code.Reference(propertyClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
190+
<#
191+
<#
192+
```
193+
126194
## Low-impact changes
127195

128196
<a name="sqlite-math"></a>
@@ -196,3 +264,128 @@ var query = dbContext.Cylinders
196264
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
197265
});
198266
```
267+
268+
<a name="type-base"></a>
269+
270+
### ITypeBase replaces IEntityType in some APIs
271+
272+
[Tracking Issue #13947](https://github.com/dotnet/efcore/issues/13947)
273+
274+
#### Old behavior
275+
276+
Previously, all mapped structural types were entity types.
277+
278+
#### New behavior
279+
280+
With the introduction of complex types in EF8, some APIs that were previously use an `IEntityType` now use `ITypeBase` so that the APIs can be used with either entity or complex types. This includes:
281+
282+
- `IProperty.DeclaringEntityType` is now obsolete and `IProperty.DeclaringType` should be used instead.
283+
- `IEntityTypeIgnoredConvention` is now obsolete and `ITypeIgnoredConvention` should be used instead.
284+
285+
#### Why
286+
287+
With the introduction of complex types in EF8, these APIs can be used with either `IEntityType` or `IComplexType`.
288+
289+
#### Mitigations
290+
291+
The old APIs are obsoleted, but will not be removed until EF10. Code should be updated to use the new APIs ASAP.
292+
293+
<a name="value-converters"></a>
294+
295+
### ValueConverter and ValueComparer expressions must use public APIs for the compiled model
296+
297+
[Tracking Issue #24896](https://github.com/dotnet/efcore/issues/24896)
298+
299+
#### Old behavior
300+
301+
Previously, `ValueConverter` and `ValueComparer` definitions where not included in the compiled model, and so could contain arbitrary code.
302+
303+
#### New behavior
304+
305+
EF now extracts the expressions from the `ValueConverter` and `ValueComparer` objects and includes these C# in the compiled model. This means that these expressions must only use public API.
306+
307+
#### Why
308+
309+
The EF team is gradually moving more constructs into the compiled model to support using EF Core with AOT in the future.
310+
311+
#### Mitigations
312+
313+
Make the APIs used by the comparer public. For example, consider this simple converter:
314+
315+
```csharp
316+
public class MyValueConverter : ValueConverter<string, byte[]>
317+
{
318+
public MyValueConverter()
319+
: base(v => ConvertToBytes(v), v => ConvertToString(v))
320+
{
321+
}
322+
323+
private static string ConvertToString(byte[] bytes)
324+
=> ""; // ... TODO: Conversion code
325+
326+
private static byte[] ConvertToBytes(string chars)
327+
=> Array.Empty<byte>(); // ... TODO: Conversion code
328+
}
329+
```
330+
331+
To use this converter in a compiled model with EF8, the `ConvertToString` and `ConvertToBytes` methods must be made public. For example:
332+
333+
```csharp
334+
public class MyValueConverter : ValueConverter<string, byte[]>
335+
{
336+
public MyValueConverter()
337+
: base(v => ConvertToBytes(v), v => ConvertToString(v))
338+
{
339+
}
340+
341+
public static string ConvertToString(byte[] bytes)
342+
=> ""; // ... TODO: Conversion code
343+
344+
public static byte[] ConvertToBytes(string chars)
345+
=> Array.Empty<byte>(); // ... TODO: Conversion code
346+
}
347+
```
348+
349+
<a name="exclude-from-migrations"></a>
350+
351+
### ExcludeFromMigrations no longer excludes other tables in a TPC hierarchy
352+
353+
[Tracking Issue #30079](https://github.com/dotnet/efcore/issues/30079)
354+
355+
#### Old behavior
356+
357+
Previously, using `ExcludeFromMigrations` on a table in a TPC hierarchy would also exclude other tables in the hierarchy.
358+
359+
#### New behavior
360+
361+
Starting with EF Core 8.0, `ExcludeFromMigrations` does not impact other tables.
362+
363+
#### Why
364+
365+
The old behavior was a bug and prevented migrations from being used to manage hierarchies across projects.
366+
367+
#### Mitigations
368+
369+
Use `ExcludeFromMigrations` explicitly on any other table that should be excluded.<a name="exclude-from-migrations"></a>
370+
371+
<a name="persist-to-cosmos"></a>
372+
373+
### Non-shadow integer keys are persisted to Cosmos documents
374+
375+
[Tracking Issue #31664](https://github.com/dotnet/efcore/issues/31664)
376+
377+
#### Old behavior
378+
379+
Previously, non-shadow integer properties that match the criteria to be a synthesized key property would not be persisted into the JSON document, but were instead re-synthesized on the way out.
380+
381+
#### New behavior
382+
383+
Starting with EF Core 8.0, these properties are now persisted.
384+
385+
#### Why
386+
387+
The old behavior was a bug and prevented properties that match the synthesized key criteria from being persisted to Cosmos.
388+
389+
#### Mitigations
390+
391+
[Exclude the property from the model](xref:core/modeling/entity-properties#included-and-excluded-properties) if its value should not be persisted.

0 commit comments

Comments
 (0)