IQueryable 是否会在每次访问对象时重新查询数据库?

Does IQueryable re-query the database on every access of the object?

我的代码中有一个错误,最终设法将其追溯到我对 IQueryable 语句的使用,但是所发生的事情的含义给我留下了一些问题我希望有人对 Entity Framework 可以阐明一些问题,以便我可以改进。

(N.B。这段代码最初是在单个 foreach 循环中,但我在那里也遇到了一个错误,虽然我不确定它是否是同一个)

IQueryable<SystemOrder> Orders = _ctx.SystemOrder.Where(x => x.ordStatus == 1 && x.rctStatus == 0).OrderBy(x => x.Id);

            Console.WriteLine(Orders.Count());

            AddressLabel l;

            foreach (SystemOrder Order in Orders)
            {
                Order.rctStatus = 1;
            }
            _ctx.SaveChanges();

            foreach (SystemOrder Order in Orders)
            {
                l = new AddressLabel(Order);
                SendOrderConfirmationEmail(Order, l);
                Order.rctStatus = 2;
            }
            _ctx.SaveChanges();

            foreach (SystemOrder Order in Orders)
            {
                l = new AddressLabel(Order);
                CreateOrderFiles(Order, l);
                Order.rctStatus = 3;
            }
            _ctx.SaveChanges();

            foreach (SystemOrder Order in Orders)
            {
                SystemOrder result = PushToTill(Order);
                Order.ordStatus = result.ordStatus;
            }
            _ctx.SaveChanges();

所以问题是结果只在第一个 foreach 循环中起作用,所有后续循环都被忽略,因为 "Orders" IQueryable 没有返回任何结果。我很困惑,直到我突然意识到它可能是 运行 每次访问订单时的查询,因为它不在内存中。将结果推送到列表并对其进行处理避免了这个问题。

如果是这种情况,我有几个问题:

  1. 我的假设是否真的正确,因为每个 foreach 都在重新查询数据库?
  2. 如果代码包含在单个 foreach 中,这不会发生吗?
  3. 在这种情况下,最好的方法是什么?使用内存或多个查询?

我需要单独的 foreach 循环的原因是我可以在每次状态更新时调用 SaveChanges(),因为您不能在循环内调用它。效率低下,但我不确定在这种情况下最好的方法。

谢谢:)

1 - 是

2 - 是的,单个 foreach 应该可以工作

3 - 不清楚是什么情况,我觉得使用single foreach是一个很好的解决方案

In situations like this, what would be the best approach? Using memory or multiple queries?

使用任何一种方法产生最易读的代码。如果有多个 foreach 循环使您的代码更具可读性,请替换

IQueryable<SystemOrder> Orders = _ctx.SystemOrder
    .Where(x => x.ordStatus == 1 && x.rctStatus == 0)
    .OrderBy(x => x.Id);

与:

List<SystemOrder> Orders = _ctx.SystemOrder
    .Where(x => x.ordStatus == 1 && x.rctStatus == 0)
    .OrderBy(x => x.Id)
    .ToList();

调用 ToList() 访问数据库(即运行一次查询)。枚举 List<T> 不会访问数据库。与许多人的直觉相反,具体化查询不会中断 _ctx.SaveChanges()。在更改跟踪的上下文中,通过 ToList() 实现查询与通过 foreach.

实现查询没有特别不同