私は単にそうしないことをお勧めします。必ずしも聞きたいとは限りませんが、特にとミキシングする場合TransactionScope
、セーブポイントは良い考えではありません。TransactionScope
s はネストできますが、最初のロールバックですべてが台無しになり、コミットは最も外側のトランザクションでのみ発生します。
私が考えることができるほとんどのシナリオでは、最初にデータをサニタイズすることをお勧めします。セーフティ ネットに制約を使用することもできます (またそうする必要があります) が、そのセーフティ ネットにヒットした場合は、大きな問題を想定してすべてをロールバックします。
ネストされたトランザクションの例:
public void DebitCreditAccount(int accountId, decimal amount, string reference)
{
using(var tran = new TransactionScope())
{
// confirm account exists, and update estimated balance
var acc = db.Accounts.Single(a => a.Id == accountId);
acc.BalanceEstimate += amount;
// add a transaction (this defines the **real** balance)
db.AccountTransactions.InsertOnSubmit(
new AccountTransaction {
AccountId = accountId, Amount = amount,
Code = amount >= 0 ? "C" : "D",
Reference = reference });
db.SubmitChanges();
tran.Complete();
}
}
public void Transfer(int fromAccountId, int toAccountId,
decimal amount, string reference)
{
using(var tran = new TransactionScope())
{
DebitCreditAccount(fromAccountId, -amount, reference);
DebitCreditAccount(toAccountId, amount, reference);
tran.Complete();
}
}
上記でDebitCreditAccount
は、アトミックです。アカウント トランザクションを追加して推定残高を更新するか、どちらも更新しません。これが唯一のトランザクションである場合、このメソッドの最後でコミットされます。
ただし、このTransfer
メソッドでは、別の外部トランザクションを作成します。両方 を実行するか、どちらも実行DebitCreditAccount
しません。ここでは、外部トランザクションがあるため、内部tran.Complete()
(in DebitCreditAccount
)は db-transaction をコミットしません。単純に「嬉しい」です。ただし、逆に、内側のトランザクションのいずれかが中止される (Dispose()
なしで呼び出されるComplete()
) 場合、外側のトランザクションはすぐにロールバックされ、そのトランザクションは追加の作業を拒否します。外部トランザクションは、内部トランザクションが中止されなかった場合にのみコミットされ、外部トランザクションで Complete()
呼び出されます。