Chindu Krishna
Chindu Krishna

Reputation: 431

Threads and linq data context

This is my code:

DatabaseDataContext context = new DatabaseDataContext();

var sourceList = (from q in context.Table1 where col1< 2000 select q).ToList();

foreach( Type x in sourceList)
{
    var task = Task.Factory.StartNew(() => FetchData(x));
}

FetchData(x)
{
    SomeBO obj = new SomeBO();

    obj.Prop1 = x.Table2.col;
    obj.Prop2 = x.Table3.col;
}

When I execute this code, I get an exception 'There is already an open DataReader associated with this Command which must be closed first'. How could I make the method 'FetchData' to execute in parallel for all the list item in 'sourceList'?

Upvotes: 0

Views: 548

Answers (3)

Gert Arnold
Gert Arnold

Reputation: 109080

You are using one context instance in multiple threads. But a context is not thread safe. Switching on MARS won't help, the tasks will keep competing for the connections.

The solution is to create a new context for each task. Maybe something like

FetchData(x)
{
    SomeBO obj = new SomeBO();
    using(var context = new DatabaseDataContext())
    {
        obj.Prop1 = context.Table2s.Where(t2 => t2.Id == x.Id)
                           .FirstOrDefault().col;
        ...
    }
}

but I have to guess, because I don't know the structure of your classes.

Upvotes: 1

cat916
cat916

Reputation: 1361

'There is already an open DataReader associated with this Command which must be closed first'

You could have a look at a link Entity Framework - There is already an open data reader associated with this command

How could I make the method 'FetchData' to execute in parallel for all the list item in 'sourceList'?

I think you want Parallel.ForEach

Upvotes: 0

Jens Kloster
Jens Kloster

Reputation: 11277

You properly need to include MARS in your connectionstring.
Like this:

 var sqlBuilder = new SqlConnectionStringBuilder
          {
            DataSource = "myServer",
            InitialCatalog = "mydatabase",
            IntegratedSecurity = false,
            UserID = "user",
            Password = "pass",
            MultipleActiveResultSets = true //<-- here
          };

and then

   var efConnString = new EntityConnectionStringBuilder
          {
            ProviderConnectionString = sqlBuilder.ToString()
           //other properties omitted
          }

Or you could do it the old-fashion-way:

string connectionString = "Data Source=MSSQL1;" + 
    "Initial Catalog=AdventureWorks;Integrated Security=SSPI" +
    "MultipleActiveResultSets=True";

Upvotes: 0

Related Questions