Reputation: 7243
I have an application that has many different types of objects that each persist themselves back to the db. This has worked fine so far without transactions and I'm not looking to go hog wild adding them. But there is an occasional need to start a transaction before a whole collection of the objects start updating to ensure that the database is only actually updated if all objects in the collection succeed.
For example, say I have a collection of apples. The command is issued to the collection to update all the apples. [transaction starting should be here] The each apples executes the code to update itself. [transaction commit/rollback should happen here].
The hitch I have is that each update is atomic right now (not explicitly wrapped in a transaction). I could pass an id to each "apple" to identify a transaction that has been stashed in some kind of cache, but then there's the risk that the cache would be invalidated mid-update and cause an unnecessary problem.
So what's the best approach to this?
Upvotes: 4
Views: 3784
Reputation: 640
Transactions are really simple in ado.net 2.0 I'd suggest using the transactionscope and let the framework mange the transaction for you:
read all about it on MSDN:
Transaction Flow Management
Transaction scopes can nest both directly and indirectly. A direct scope nesting is simply one scope nested inside another, as shown in Example 5.
Example 5. Direct scope nesting
using(TransactionScope scope1 = new TransactionScope()) { using(TransactionScope scope2 = new TransactionScope()) { scope2.Complete(); } scope1.Complete(); }
An indirect scope nesting occurs when calling a method that uses a
TransactionScope
from within a method that uses its own scope, as is the case with theRootMethod()
in Example 6.Example 6. Indirect scope nesting
void RootMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ SomeMethod(); scope.Complete(); } } void SomeMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ scope.Complete(); } }
Upvotes: 1
Reputation: 25330
The OP says that the collection manages the save of all of the other objects in the transaction so it would seem obvious to put the transaction code here. If you are using ADO.Net, it would seem the easiest option to open the connection and begin the transaction in the collection and then just pass this to each of the other objects. I am assuming that each of the objects here inherits from a Layer Supertype Class and you are using SQL Server.
public void Save()
{
using (SqlConnection connection = new SqlConnection("Connection String"))
{
using (SqlTransaction trans = connection.BeginTransaction())
{
foreach (BusinessObject obj in this)
{
obj.Save(connection);
}
trans.Commit();
}
}
}
Upvotes: 1
Reputation: 17260
First, I wouldn't be handling the transactional logic in the page. Write a business class of some sort to do this - a service, a data utility class, something you can abstract away from ASP.Net.
Next, you might look at using the TransactionScope class (in System.Transactions namespace, reference System.Transactions.dll) if you are using a database that can subscribe to distributed transaction like SQL Server.
using(TransactionScope scope = new TransactionScope())
{
SaveObjectOne(); //these are just psuedo-code statements
SaveObjectTwo(); //replace these with your code that saves various objs
SaveObjectThree();
scope.Complete(); //this commits the transaction, unless exception throws
}
TransactionScope implements IDisposable, so when using calls Dispose() the transaction will roll back if Complete() was never called. You do need to enable the Distributed Transaction Coordinator to use TransactionScope.
Upvotes: 4
Reputation: 3293
Upvotes: 0