使用Linq to SQL进行多线程处理

时间:2009-11-29 13:23:17

标签: c# multithreading linq-to-sql datacontext

我正在构建一个应用程序,它要求我使用DataContext的内部线程。我的应用程序不断抛出InvalidOperationException类似于:

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());

欢迎改进/建议,但这似乎已经成功了。

4 个答案:

答案 0 :(得分: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()中有很多逻辑且查询不是很重,那么这个解决方案很好。

答案 1 :(得分:2)

尝试在新线程中创建存储库,而不是将其传入。

答案 2 :(得分:1)

请注意,SqlConnection实例不是线程安全的。你是否仍然有一个开放的阅读器。通常,从多个线程访问SqlConnection实例将导致不可预测的间歇性问题。

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

答案 3 :(得分:0)

我的解决方案是LightSpeed Persistence框架,它在8个实体之前是免费的。每个线程创建单元。

http://www.mindscapehq.com/products/LightSpeed/default.aspx