2009-11-29 65 views
4

我正在构建一个需要我使用DataContext内部线程的应用程序。我的应用程序不断抛出InvalidOperationException的相似:Linq to SQL多线程

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

ExecuteReader requires an open and available Connection. The connection's current state is connecting

这些例外是间歇的。

这里是我的代码片段:

var repo = new Repository(); 
var entities = repo.GetAllEntities(); 
foreach (var entity in entities) 
{ 
    ThreadPool.QueueUserWorkItem(
     delegate 
     { 
      try 
      { 
       ProcessEntity(entity); 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
     }); 
} 

我认为它可能有一些与从主线程传递一个实体的线程的错误似乎只要我试图访问扔财产entity

任何人有任何想法,为什么发生这种情况,我如何解决它?

更新

这是我决定去:

var events = new Dictionary<int, AutoResetEvent>(); 
var repo = new Repository(); 
var entities = repo.GetAllEntities(); 
foreach (var entity in entities) 
{ 
    events.Add(entity.ID, new AutoResetEvent(false)); 
    ThreadPool.QueueUserWorkItem(
     delegate 
     { 
      var repo = new Repository(); 
      try 
      { 
       ProcessHierarchy(repo.GetEntity(entity.ID), ReportRange.Weekly); 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
      finally 
      { 
       events[entity.ID].Set(); 
      } 
     }); 
} 

WaitHandle.WaitAll(events.Values.ToArray()); 

改进/建议表示欢迎,但这似乎已经完成了帽子戏法。

+0

这本字典有一个名为 “值” 属性。 – Henrik 2009-11-29 19:38:40

+0

@亨利克,谢谢你从未注意到! – James 2009-11-29 20:09:02

+1

+1表示与传入实体相关的建议。解决了我的问题欢呼声。 – 2011-05-26 06:06:10

回答

7

抛出异常,因为当一个前面的阅读器尚未关闭实体的某些属性,执行新的查询。您无法同时在数据上下文中执行多个查询。

作为解决方法,您可以“访问”您在ProcessEntity()中访问的属性,并使SQL在线程之前运行。

例如:

var repo = new Repository(); 
var entities = repo.GetAllEntities(); 
foreach (var entity in entities) 
{ 
    var localEntity = entity; // Verify the callback uses the correct instance 
    var entityCustomers = localEntity.Customers; 
    var entityOrders = localEntity.Orders; 
    var invoices = entityOrders.Invoices; 
    ThreadPool.QueueUserWorkItem(
     delegate 
     { 
      try 
      { 
       ProcessEntity(localEntity); 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
     }); 
} 

此解决方案将执行仅在主线程和处理SQL将在其他线程来完成。由于所有查询都是在单线程中完成的,因此您可以省去一些效率。如果在ProcessEntity()中有很多逻辑并且查询不是很重,那么这个解决方案很好。

+0

因此,您的提示我提前检索了所有可能的数据,我需要在我的'ProcessEntity'方法中进行处理?当然必须有更好的方法?在线程内部使用DataContexts不够安全吗? – James 2009-11-29 14:26:29

+0

更好的方法是在一个上下文中执行对实体的查询,并在每个WorkItem中启动新的DataContext,但是如果DataContext不使用某种连接池,则可以轻松达到最大连接数并遇到新的异常:) 另一个解决方案是使用反射访问标记为数据成员的所有属性,并避免显式访问属性。它会表现相同,你的数据库查询将不会在后台线程中运行... – Elisha 2009-11-29 14:41:32

+0

啊,所以在我的每个匿名代表中再次检索实体并仅仅处理该数据上下文!目前不太可能(当前时间)连接会达到最大值,但有没有更好的方式来管理连接大小?我虽然SQL内部管理? – James 2009-11-29 14:48:57

2

尝试建立新的线程里面的仓库,而不是通过它的。

+0

我需要找到它之外的实体。但是,我想我可以发送实体的ID并在线程中重新获取它。 – James 2009-11-29 13:33:12

+1

这听起来像是你最好的选择,我不认为它可以安全地通过你想要做的事情传递ObjectContext实例。 – Rory 2009-11-29 13:35:20

+1

@詹姆斯给出了为线程创建工作项目的方式(使用匿名委托),您应该可以依靠c#闭包支持来捕获ID的值,而不必将其传入。有关更多信息,请访问http://blogs.msdn.com/matt/archive/2008/03/01/understanding-variable-capturing-in-c.aspx。 – Rory 2009-11-29 14:02:07

1

请注意,SqlConnection实例不是线程安全的。你是否仍然拥有一个开放的读者。一般来说,从多个线程访问SqlConnection实例会导致不可预知的间歇性问题。

请参见:http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx

+2

那么当尝试在这种情况下开发多线程解决方案时,最佳做法是什么?正如以利沙建议事先取回所有数据? – James 2009-11-29 19:15:55