Reputation: 926
Given a TransactionScope with 2 subsequently opened DbContexts, are changes saved by the first context guaranteed to be visible within the scope of second context?
var txOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
using (var transaction = new TransactionScope(TransactionScopeOption.Required, txOptions))
{
using (var context1 = new MyDbContext())
{
context1.Employees.Single(e => e.Id == 1).Salary += 1; // Update
context1.Employees.Remove(context1.Employees.Single(e => e.Id == 2)); // Delete
context1.Employees.Add(new Employee { Id = 3 }); // Add
context1.SaveChanges();
}
using (var context2 = new MyDbContext())
{
// are changes saved by context1 guaranteed to be visible here?
}
transaction.Complete();
}
Normally I would expect they are, but then I saw "No, this was a coincidence because the 2nd context reused the connection of the 1st from the connection pool. This is not guaranteed and will break under load." which made me puzzled. Could anyone please confirm or disprove this?
Upvotes: 1
Views: 351
Reputation: 107407
My understanding is that all efforts will be made to ensure that your thread receives the same connection from the pool, provided that you close each connection prior to requesting another, and provided that the connection strings are identical. Reusing the same connection will prevent the transaction from escalating to DTC.
However if you still want further guarantees, there is an overload of the DbContext constructor which takes an existing connection. In this way, you would be able to guarantee that the two Contexts use the same Connection. This way you don't need to worry about the behaviour of the lightweight transaction manager. IMO you'd be best off opening your own connection, and passing this to both contexts, with the contextOwnsConnection
flag set to false.
(Hopefully the below is all Hypothetical)
The consequences of not reusing the connection are dire - if context2
was not able to reuse the same connection as context1
, the bounding TransactionScope
would escalate into a distributed transaction, which would in any case cause further potential lock + deadlock issues.
i.e. in your example, the 3 rows associated with the modified employees in context1
would be blocked to a second database connection on context2
with a ReadCommited
isolation level, until the Transaction is either committed, or rolled back (i.e. your program could hang until the transaction, or command, timed out).
I guess one burning question - since both contexts use the same database, if possible, try to combine the two contexts. This way you can avoid the bounding TransactionScope
altogether - SaveChanges()
acts as a single phase transaction against a single connection.
Upvotes: 1