I have a parent object (part of a DAL) that contains, amongst other things, a collection (List
) of child objects.
When I\'m saving the object b
Empirically, I have determined that (for the SQL Server provider) if the process can take advantage of connection pooling to share the connection (and the transaction) between the parent and child processes, the DTC will not necessarily become involved.
This is a big "if", however, as per your example, the connection created by the parent process cannot be shared by the child processes (you do not close/release the connection before invoking the child processes). This will result in a transaction that spans two actual connections, which will result in the transaction being promoted to a distributed transaction.
It seems that it would be easy to refactor your code to avoid this scenario: just close the connection created by the parent process before invoking the child processes.
Many database ADO providers (such as Oracle ODP.NET) do indeed begin distributed transactions when you use TransactionScope
to transact across multiple connections - even when they share the same connection string.
Some providers, (like SQL2008 in .NET 3.5+) recognizes when a new connection is created in a transaction scope that refers to the same connection string, and will not result in DTC work. But any variance in the connection string (such as tuning parameters) may preclude this from occuring - and the behavior will revert to using a distributed transaction.
Unfortunately, the only reliable means of ensuring your transactions will work together without creating a distributed transaction is to pass the connection object (or the IDbTransaction
) to methods that need to "continue" on the same transaction.
Sometimes it helps to elevate the connection to a member of the class in which you're doing the work, but this can create awkward situations - and complicates controlling the lifetime and disposal of the connection object (since it generally precludes use of the using
statement).
In your example the TransactionScope is still in the context of a method, you could simply create a SqlTransaction with multiple commands beneath that. Use TransactionScope if you want to move the transaction out of a method, to say, the caller of that method, or if you access multiple databases.
Update: never mind I just spotted the child call. In this situation, you could pass the connection object to child classes. Also, you don't need to manually dispose the TransactionScope - using blocks act like try-finally blocks and will execute the dispose even on exceptions.
Update 2: better yet, pass the IDbTransaction to the child class. The connection can be retrieved from that.