Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-threading with Linq to SQL

Since the original thread (Multi-threading with Linq to SQL) has become quite old by now, I've thought I'd post another question on a similar subject. Consider a scenario, where a DomainService exposes numerous methods to retrieve data from a SQL Server database. Obviously, in a multi user scenario, with multiple requests coming in at the same time, one has to expect this.DataContext to be used in parallel, with no control nor additional effort from the developer to handle those multiple requests. So how come, if I put my sequential LINQ queries into a Parallel.Invoke(), all hell breaks loose and I get the dreadded "There is already an open DataReader associated with this Command which must be closed first." error ...?

To demonstrate, this works:

List<Data> retVal = new List<Data>();

retVal.AddRange(this.DataContext.Table1.Where(w=>w.A==1).Select(s=>new Data{f1=s.D}).ToList());
retVal.AddRange(this.DataContext.Table1.Where(w=>w.B==2).Select(s=>new Data{f1=s.D}).ToList());
retVal.AddRange(this.DataContext.Table1.Where(w=>w.C==3).Select(s=>new Data{f1=s.D}).ToList());

... and yet this doesn't:

List<Data> retVal = new List<Data>();
Parallel.Invoke(
()=>retVal.AddRange(this.DataContext.Table1.Where(w=>w.A==1).Select(s=>new Data{f1=s.D}).ToList()),
()=>retVal.AddRange(this.DataContext.Table1.Where(w=>w.B==2).Select(s=>new Data{f1=s.D}).ToList()),
()=>retVal.AddRange(this.DataContext.Table1.Where(w=>w.C==3).Select(s=>new Data{f1=s.D})).ToList());

Never mind for a second that List is not thread-safe, as the error is coming from the SQL data connection.

Any insights and explanations will be much appreciated.

like image 612
Darek Avatar asked Jan 05 '11 15:01

Darek


2 Answers

First, to clarify, this issue is to do with multi-threading rather than multi-users. In a multi-user scenario, each user will have their own DataContext instance, avoiding threading issues around shared instances.

The parallel example fails because the DataContext is not a thread-safe object; it expects to be used by a single thread, not by many in parallel. This emerges as an exception associated with data readers, because the DataContext has its connection open, reading with the data reader when you attempt to execute the second statement in parallel.

The same issue would be evident if you tried to use one SqlConnection instance across several threads without any serialization techniques.

like image 143
Paul Turner Avatar answered Sep 30 '22 15:09

Paul Turner


You shouldn't share a DataContext across threads. It is inherently unsafe. Additionally, DataContexts are meant to be used one per unit of work (i.e., one per conversation). Each request should be considered a different conversation and should be answered with a unique DataContext.

like image 32
jason Avatar answered Sep 30 '22 16:09

jason