Fixed: "The connection is already in a transaction and cannot participate in another transaction" in EF Core
Calling one helper method from inside a transaction failed with The connection is already in a transaction and cannot participate in another transaction. The helper had its own BeginTransactionAsync, which made sense when it was written, and it stopped making sense once something else called it inside a transaction.
What I Was Doing
In my .NET C# Web API with EF Core 10.0.14, an endpoint started a transaction and called a helper that changes stock:
g.MapPost("/tx/helper-begins", async (InventoryDbContext db, CancellationToken cancellationToken) =>
{
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
await AdjustStockWithOwnTransactionAsync(db, 1, cancellationToken);
await transaction.CommitAsync(cancellationToken);
return Results.Ok();
});
The helper had a transaction of its own, because it had first been written to stand alone:
static async Task AdjustStockWithOwnTransactionAsync(InventoryDbContext db, int productId, CancellationToken cancellationToken)
{
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
await db.Products
.Where(p => p.Id == productId)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.ReorderLevel, p => p.ReorderLevel + 1), cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
What I Expected
The helper's work to be part of the outer transaction.
What I Got
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "An error occurred while processing your request.",
"status": 500,
"traceId": "00-b9a6979c1091e22bdda1066822c03c84-d260a45be8968b15-00"
}
System.InvalidOperationException: The connection is already in a transaction and cannot participate in another transaction.

What I Checked
Which call fails. The second BeginTransactionAsync, in the helper. The log shows the first one beginning, and nothing has run yet:
Beginning transaction with isolation level 'Unspecified'.
Began transaction with isolation level 'Unspecified'.
The EF Core docs. The transactions page shows one transaction per connection, and savepoints for marking points inside it.
What MySQL offers. MySQL has no nested transactions. START TRANSACTION inside a transaction commits the first one, so EF Core stopping this is a favour.
Why It Happens
A DbContext has one connection, and a connection has one transaction at a time. The message says exactly that. When a helper method and its caller share a scoped DbContext from dependency injection, they share that connection too.
The Fix
Let one place own the transaction, and let everything it calls work inside it. The helper loses its transaction:
static async Task AdjustStockAsync(InventoryDbContext db, int productId, CancellationToken cancellationToken)
{
await db.Products
.Where(p => p.Id == productId)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.ReorderLevel, p => p.ReorderLevel + 1), cancellationToken);
}
And the endpoint calls it inside its own transaction:
g.MapPost("/tx/helper-joins", async (InventoryDbContext db, CancellationToken cancellationToken) =>
{
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
await AdjustStockAsync(db, 1, cancellationToken);
await transaction.CommitAsync(cancellationToken);
return Results.Ok();
});
If the helper must also work alone, its caller starts the transaction in both cases. If you need a part of the work to fail without losing the rest, use a savepoint.
After the Fix
HTTP/1.1 200 OK
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200]
Began transaction with isolation level 'Unspecified'.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202]
Committed transaction.

Common Mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
Using a second DbContext for the helper | That's a second connection, and a second transaction | Share one, unless you mean to |
Sending START TRANSACTION as raw SQL | MySQL commits the first one | Use one transaction |
| Committing in the helper | The caller's later work runs outside it | Commit in one place |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
does not support user-initiated transactions | The transaction is outside the strategy | The fix |
Connection must be valid and open to commit transaction | A second commit | The fix |
| It only happens from one caller | That caller has a transaction open | Check db.Database.CurrentTransaction |
Conclusion
A DbContext's connection holds one transaction at a time. Begin it in one place, and let the methods it calls run inside it.
Additional Resources
- Using Transactions — transactions and savepoints.
- START TRANSACTION, COMMIT, and ROLLBACK — MySQL's side.
