I have the following code, intended to break a bulk EF save into smaller chunks, ostensibly to improve performance.
var allTasks = arrayOfConfigLists
.Select(configList =>
Task.Run(() => SaveConfigurations(configList))
.ToArray();
Task.WaitAll(allTasks);
Each call to SaveConfigurations creates a new context that runs to completion.
private static void SaveConfigurations(List<Configuration> configs)
{
using (var dc = new ConfigContext())
{
dc.Configuration.AutoDetectChangesEnabled = false;
dc.SaveConfigurations(configs);
}
}
As it stands, the code runs relatively efficiently, considering this might not be the optimal way of doing things. If one of the SaveConfigurations fails, however, I realized I would need to roll back any other configurations that were saved to the database.
After doing some research, I upgraded my existing frameworks to 4.5.1 and utilized the new TransactionScopeAsyncFlowOption.Enabled option to deal with async calls. I made the following change:
using (var scope =
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
//... allTasks code snippet from above
scope.Complete();
}
At this point, I started aggregating all kinds of interesting errors:
The operation is not valid for the state of the transaction.
The underlying provider failed on Open.
Network access for Distributed Transaction Manager (MSDTC) has been disabled.
The transaction manager has disabled its support for remote/network transactions.
What I don't understand is why introducing TransactionScope would create so many issues. I assume I have a fundamental misunderstanding of how async calls interact with EF, and how TransactionScope wraps those calls, but I can't figure it out. And I really have no clue what the MSDTC exception pertains to.
Any thoughts as to how I could have rollback functionality with asynchronous calls made to the same database? Is there a better way to handle this situation?
Update: After reviewing the documentation here, I see that Database.BeginTransaction() is the preferred EF call. However, this assumes that all of my changes will occur within the same context, which it won't. Short of creating a dummy context and passing around a transaction, I don't believe this solves my issue.