Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This page documents API and behavior changes that have the potential to break ex
| [Cosmos: the default discriminator property is now named `Discriminator` in the model](#cosmos-discriminator-property-name) | Low |
| [Owned JSON collections without an explicit key are obsolete](#owned-json-collections-obsolete) | Low |
| [`Property` no longer configures primitive collections](#property-not-primitive-collection) | Low |
| [Split queries now throw when concurrent modifications are detected](#split-query-concurrency-exception) | Low |

## Medium-impact changes

Expand Down Expand Up @@ -479,6 +480,69 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

In most cases no change is required, since primitive collections are discovered by convention.

<a name="split-query-concurrency-exception"></a>

### Split queries now throw when concurrent modifications are detected

[Tracking Issue #33826](https://github.com/dotnet/efcore/issues/33826)

#### Old behavior

Previously, when a split query (using `AsSplitQuery()`) encountered out-of-order or orphaned child rows caused by concurrent data modifications between the split query's SQL statements, EF Core silently discarded the affected child collections. The result was an entity with an empty collection even though the parent and its related data were never modified—no exception was thrown and no warning was logged.

#### New behavior

Starting with EF Core 11.0, EF Core throws a `DbQueryConcurrencyException` when split query results cannot be correlated because of concurrent data modifications. The exception message describes the situation and suggests remediation:

> The results of a split query could not be correlated because the data was modified concurrently while the query was executing. Re-execute the query, or execute it within a serializable or snapshot transaction to prevent concurrent modifications.

#### Why

Silently returning incorrect data (empty collections for entities that have related rows) is far worse than surfacing an error. This scenario is intrinsically caused by the lack of data-consistency guarantees in split queries when the database is modified between statements. Throwing a retriable exception makes the problem visible and gives callers a clear path to recovery.

#### Mitigations

The simplest mitigation is to re-execute the query; the concurrent modification is transient and the retry will typically succeed:

```csharp
List<Blog> blogs;
while (true)
{
try
{
blogs = await context.Blogs
.Include(b => b.Posts)
.AsSplitQuery()
.ToListAsync();
break;
}
catch (DbQueryConcurrencyException)
{
// Retry on concurrent modification
}
}
```

Alternatively, wrap the split query in a serializable or snapshot transaction to prevent concurrent modifications from affecting the results:

```csharp
await using var transaction = await context.Database.BeginTransactionAsync(IsolationLevel.Snapshot);

var blogs = await context.Blogs
.Include(b => b.Posts)
.AsSplitQuery()
.ToListAsync();
```

If neither retry nor a transaction is acceptable, switch to a single query (`AsSingleQuery()`) which is always consistent:

```csharp
var blogs = await context.Blogs
.Include(b => b.Posts)
.AsSingleQuery()
.ToListAsync();
```

<a name="MDS-breaking-changes"></a>

## Microsoft.Data.Sqlite breaking changes
Expand Down