I have a situation (I’m guessing is pretty standard) where I need to perform some business calculations and create a bunch of records in the database. If anything goes wrong at any point I need to roll everything back from the database. Obviosly I need some kind of transaction. My question is where do I implement transaction support. Here’s my example
//BillingServices - This is my billing service layer. called from the UI public Result GenerateBill(BillData obj) { //Validate BillData //Create a receivable line item in the receivables ledger BillingRepository.Save(receivableItem); //Update account record to reflect new billing information BillingRepository.Save(accountRecord); //...do a some other stuff BillingRepository.Save(moreStuffInTheDatabase); }
If any of the updates to the database fail I need to roll the other ones back and get out. Do I just expose a Connection object through my Repository in which I can call
Connection.BeginTransaction()
or do I do I just validate in the service layer and just call one method in the repository that saves all the objects and handles the transaction? This doesn’t quite seem right to me. It’s seem like it would force me to put to much business logic in the data layer.
What’s the right approach? What if I need to span repositories (or would that be bad design)?
I’m assuming that you are using .NET here. That being the case, you can simply wrap the entire code section in a
usingstatement with aTransactionScopeinstance and it will handle the transaction semantics for you. You simply have to call theCompletemethod at the end:If an exception occurs, this has the effect of not calling
Completewhen the code block is exited; theDisposemethod on theTransactionScopeimplementation of theIDisposableinterface is called when the scope of theusingstatement is exited.In the
Disposecall, it checks to see if the transaction completed (this state is set whenCompletesucceeds). If that state isn’t set, it performs a rollback.You can then nest this within other
TransactionScopeinstances (deeper in your call stack on the same thread) to create larger transactions across multiple repositories.