Có. Một lời gọi SaveChanges chạy toàn bộ thay đổi trong một transaction ngầm: hỏng một câu là rollback tất cả. Nên không cần bọc transaction thủ công quanh một SaveChanges đơn lẻ.
Cần BeginTransaction khi muốn nhiều SaveChanges (hoặc SaveChanges + raw SQL / ExecuteUpdate) là một đơn vị nguyên tử — hai lời gọi SaveChanges không tự gộp vào chung transaction.
Gotcha khi bật connection resiliency: với EnableRetryOnFailure(), mỗi thao tác EF trở thành một đơn vị retry độc lập. Execution strategy không biết cách phát lại cả khối lệnh bạn tự mở, nên ném:
> InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user-initiated transactions.
Cách đúng là giao nguyên khối cho execution strategy qua một delegate để nó phát lại được:
var strategy = db.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var tx = await db.Database.BeginTransactionAsync();
db.Orders.Add(order);
await db.SaveChangesAsync();
db.Payments.Add(payment);
await db.SaveChangesAsync();
await tx.CommitAsync();
});Lưu ý delegate có thể chạy nhiều lần, nên bên trong không được có side effect ngoài DB (gửi mail, bắn message) — đưa chúng ra sau khi commit thành công.