在Parallel.ForEach中:使用EF5时出现“底层提供程序在打开时失败”的错误。

4

这段代码有时可以处理一些项目,但当尝试处理更多项目时,它总是会失败,会得到这个异常:{"The underlying provider failed on Open."}

  List<Recon> scenarioAll = db.Transactions
                  .Where(t => t.SrcObjTyp == "13")
                  .Select(t => t.Recon).ToList();


  //db.Transactions.Where(t => t.SrcObjTyp == "13").ToList().ForEach(t => reconsWithType13Trans.Add(t.Recon));

  Parallel.ForEach(scenarioAll.Take(100), r =>
  {

    // ### Exception : {"The underlying provider failed on Open."} here ###
    invoices = r.Transactions.SelectMany(t => t.InvoiceDetails).ToList();


    CreateFacts(invoices, r/*, db*/).ForEach(f => facts.Add(f));
    transactions = r.Transactions.Where(t => !t.SrcObjTyp.Contains("13")).ToList();
    DistributeTransactionOnItemCode(transactions, facts);
    Console.WriteLine(i += 1);
  });

  facts.ForEach(f => db.ReconFacts.Add(f));
  db.SaveChanges();

我认为问题在于多个进程同时访问数据库,是否有可能允许EF通过这种方式被多个进程查询?

还有一种方法是将所有内容加载到内存中,这样当访问Recon的子级时,例如r.Transactions.SelectMany(t => t.InvoiceDetails).ToList();,所有内容都将在内存中,而不是访问底层数据库?

你认为应该使用什么解决方案?你认为哪个最好?

1个回答

2

您的问题在于有多个线程尝试进行延迟加载。 尝试用以下代码替换您的加载查询...

List<Recon> scenarioAll = db.Transactions
              .Where(t => t.SrcObjTyp == "13")
              .Select(t => t.Recon)
              .Include(r => r.Transactions.Select(t => t.InvoiceDetails))
              .ToList();

http://msdn.microsoft.com/zh-cn/library/gg671236(v=vs.103).aspx

一般来说,如果你依赖延迟加载,那么做法可能是错误的。

或者,如果你不需要使用事务,也可以这样做...

 var query =  = db.Transactions
              .Where(t => t.SrcObjTyp == "13")
              .Select(t => t.Recon);
 var scenarioAll = query.ToList();
 var invoicesByReconIdQuery = from r in query
                              from t in r.Transactions
                              from i in t.InvoiceDetails
                              group i by r.Id into g
                              select new { Id = g.Key, Invoices = g.ToList() };


 var invoicesByReconId = invoicesByReconIdQuery.ToDictionary(x => x.Id, x => x.Invoices);

 Parallel.ForEach(scenarioAll.Take(100), r =>
 {

     // ### Exception : {"The underlying provider failed on Open."} here ###
     var invoices = invoicesByReconId[r.Id];
 }

好像是个不错的答案,谢谢。但现在我得到一个“超时已过期”的异常...您知道我怎么可以处理它吗? - Arno 2501
你可以查看生成的SQL并尝试添加索引来修复它。但归根结底,你将要返回的数据集要大得多。试着弄清楚这是带宽(无法修复)问题还是延迟问题(你可以优化查询)。 - Aron
2
PS:你的.Take(100) 应该在.ToList()之前...现在你正在从数据库中取出所有数据,然后迭代前100个... 你真正想要的是只请求100行数据。 - Aron
你说得对,我已经想通并进行了更改。谢谢。 - Arno 2501

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接