Транзакции в .NET: DbTransaction, TransactionScope
↑ 4.1 ADO.NET, Npgsql и Dapper · ← Предыдущая · → Следующая
Зачем это на собесе
Про ACID на уровне кода: где границы транзакции и почему TransactionScope + async опасен.
Объяснение
await using var conn = await dataSource.OpenConnectionAsync(ct);
await using var tx = await conn.BeginTransactionAsync(IsolationLevel.ReadCommitted, ct);
try
{
await conn.ExecuteAsync("update accounts set balance = balance - @a where id = @from", new { a, from }, tx);
await conn.ExecuteAsync("update accounts set balance = balance + @a where id = @to", new { a, to }, tx);
await tx.CommitAsync(ct);
}
catch
{
await tx.RollbackAsync(ct);
throw;
}TransactionScope
using var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
TransactionScopeAsyncFlowOption.Enabled); // обязателен для async
// ... операции нескольких вызовов...
scope.Complete();Неявно привлекает все соединения в «ambient» транзакцию. Несколько разных соединений/БД → эскалация до распределённой (MSDTC), что в .NET Core на Linux неподдерживаемо и не рекомендуется.
| Уровень изоляции | Аномалии, которые возможны |
|---|---|
| Read Uncommitted | грязное чтение (в PG = Read Committed) |
| Read Committed | неповторяемое чтение, фантомы |
| Repeatable Read | фантомы (в PG — нет, snapshot) |
| Serializable | нет; возможны ошибки сериализации, нужен retry |
EF Core
SaveChanges уже выполняется в транзакции. Явная: db.Database.BeginTransactionAsync(); стратегия повторов (EnableRetryOnFailure) требует оборачивать транзакции в CreateExecutionStrategy().ExecuteAsync.
Нюансы и подводные камни
- Держите транзакцию короткой: не вызывайте внешний HTTP внутри.
- Без
TransactionScopeAsyncFlowOption.Enabledв async-коде получите исключение или потерю транзакции. - Между сервисами и БД распределённые транзакции заменяют сага и outbox (см. этап 8).
- При deadlock/serialization failure — повторяйте транзакцию целиком.
- Забытый
Complete()= откат.
Практика
- Реализуйте перевод денег с откатом при ошибке.
- Воспроизведите неповторяемое чтение на двух сессиях psql.
- Обработайте serialization failure ретраем.
Вопросы с ответами
Что такое ACID?
Атомарность, согласованность, изолированность, долговечность.
Чем плох TransactionScope?
Неявность и риск распределённых транзакций; в async нужен
AsyncFlowOption.Enabled.
Какой уровень изоляции по умолчанию в PostgreSQL?
Read Committed.