`迭代查询结果时,已经存在一个打开的DataReader`错误

时间:2016-12-06 07:52:59

标签: c# entity-framework ado.net

在我的foreach中,我在每次迭代中分配了一个我需要的新var,但是我收到一个错误:

 public ActionResult question(int? id)
 {
      if (id == null)
      {
          return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      var announcedAmount = db.AnnouncedAmount.Find(id);
      if (announcedAmount == null)
      {
          return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      decimal amount = announcedAmount.AmountAnnounced;
      var statusOne = db.Dabstats.Single(a => a.StatusName == "Request");
      var statusTwo = db.Dabstats.Single(a => a.StatusName == "Sold");

      var requests = db.Requests.Where(a => a.annId == id && a.statId == statusOne.statId).OrderBy(a => a.OfferRate);
      foreach (var item in requests)
      {
           decimal sold = requests.Where(a => a.statId == statusTwo.statId).Sum(a => a.soldAmount);
           decimal available = amount - sold;
           if (available >= item.reqAmount)
           {
                item.soldAmount = item.reqAmount;
                item.StatusId = statusOne.StatusId;
           }
           db.SaveChanges();
      }

      return RedirectToAction("Details", new { id = AnnouncedAmount.annId});
}

错误行是:

decimal sold = requests.Where(a => a.statId == statusTwo.statId).Sum(a => a.soldAmount);

错误信息:

  

已经有一个与此命令关联的开放DataReader   必须先关闭。

任何帮助,非常感谢

3 个答案:

答案 0 :(得分:4)

您创建foreach循环的方式 - 每次迭代都会读取数据库。一次查询数据库,它应该工作:

foreach (var item in requests.ToList())

当您查询数据库DataReader再次发送给数据库时,requests查询的requests.Where(a => a.statId == statusTwo.statId).Sum(a => a.soldAmount)仍处于打开状态。

如果您将ToList()添加到初始查询中,例如:

var requests = db.Requests
                 .Where(a => a.annId == id && a.statId == statusOne.statId)
                 .OrderBy(a => a.OfferRate)
                 .ToList();

只查询DB一次,循环中的计数在内存中完成。

<强>更新
你想要的是以下内容:

var requests = db.Requests
                 .Where(a => a.annId == id && a.statId == statusOne.statId)
                 .OrderBy(a => a.OfferRate)
                 .ToList();
foreach (var item in requests)
{
       decimal sold = db.Requests
                        .Where(a => a.annId == id && a.statId == statusTwo.statId)
                        .Sum(a => a.soldAmount);
       decimal available = amount - sold;
       if (available >= item.reqAmount)
       {
            item.soldAmount = item.reqAmount;
            item.StatusId = statusOne.StatusId;
       }
       db.SaveChanges();
  }

答案 1 :(得分:1)

如上所述,执行循环时,执行数据库读取的基础DbDataReader处于打开状态。在循环内部执行另一个查询,因此在连接打开时请求新的结果集。默认情况下,ADO.Net不允许这样做。有几种方法可以解决这个问题:

  1. 在连接字符串中设置MultipleActiveResultSets=trueMARS)。

  2. 在循环开始之前实现查询。现在在循环内部,您可以开始新的读取。

  3. 重新组织您的代码。仔细观察,在我看来,您不需要重复阅读已售出的金额。您可以在本地跟踪available(除非此数量由并发用户在外部更改):

    var requests = db.Requests.Where(a => a.annId == id && a.statId == statusOne.statId).OrderBy(a => a.OfferRate);
    decimal sold = requests.Where(a => a.statId == statusTwo.statId).Sum(a => a.soldAmount);
    decimal available = amount - sold;
    foreach (var item in requests)
    {
        if (available >= item.reqAmount)
        {
            item.soldAmount = item.reqAmount;
            item.StatusId = statusOne.StatusId;
            available -= item.reqAmount;
        }
    }
    db.SaveChanges();
    
  4. 在循环之前实现查询可能看起来很有吸引力,但请记住,您将数据拉入内存,而循环期间的读取则以流模式执行查询。如果涉及大量数据,这可能是一个考虑因素。

答案 2 :(得分:-1)

您可以使用以下格式:

foreach (var item in Requests)
            {
              var itemEdit = db.Requests.where(s=>s.Id == item.Id).FirstOrDefault();
                decimal sold = Requests.Where(a => a.statId == StatusTwo.statId).Sum(a => a.soldAmount);
                decimal available = amount - sold;
                if (available >= item.reqAmount)
                {
                    itemEdit.soldAmount = item.reqAmount;
                    itemEdit.StatusId = StatusOne.StatusId;
                }
                db.Entry(itemEdit).State = EntityState.Modified;
                db.SaveChanges();
            }