I have a problem with multithreaded transaction and entity framework. I have a thread, which operates in transaction and I would like to have a few more worker threads working within same transaction. The following code illustrates situation (there is one dummy entity in EF context, the code basically spawns 5 threads, I would like to insert some entities within each thread and at the end in main thread, I would like to continue working with DB, but to keep whole process isolated in ONE transaction):
using(var scope = new TransactionScope())
{
int cnt = 5;
ManualResetEvent[] evt = new ManualResetEvent[cnt];
for(int i = 0; i < cnt; i++)
{
var sink = new ManualResetEvent(false);
evt[i] = sink;
var tr = Transaction.Current.DependentClone(
DependentCloneOption.BlockCommitUntilComplete);
Action run = () =>
{
using (var scope2 = new TransactionScope(tr))
{
using (var mc = new ModelContainer())
{
mc.EntitySet.Add(new Entity()
{
MyProp = "test"
});
mc.SaveChanges();
}
}
sink.Set();
};
ThreadPool.QueueUserWorkItem(r => run());
}
ManualResetEvent.WaitAll(evt);
using (var mc = new ModelContainer())
{
Console.WriteLine(mc.EntitySet.Count());
}
Console.ReadKey();
}
The problem is, that exception is thrown on mc.SaveChanges();. Inner exception is TransactionException: “The operation is not valid for the state of the transaction.” It seems that at some point, transaction is aborted. I think it is after first thread calls SaveChanges(), but Im not sure. Any idea why transaction is aborted?
I discovered what was the problem happening here. Based on this article, I found that it is impossible to work with two MSSQL server connections within one transaction at the same time. I also discovered that I was not properly handling dependent transaction in previous code. My working illustration code follows:
It is probably not very nice way of writing multithreaded bussiness layer, but this shared transaction approach is WERY useful for testing in my case. The only modification need to make this work is to override Db context and synchronize save method in test runs.