实体框架有时不加载子对象/集合的内容
本文关键字:集合 对象 框架 加载 实体 | 更新日期: 2023-09-27 18:06:07
我已经开始有一个问题,在实体框架中的子集合没有被正确加载与延迟加载。
这方面最突出的例子是我的Orders对象——每个Order都有一个或多个与之关联的Order line。已订购的产品和数量的列表)。有时,当程序运行时,您可以打开一些订单,所有订单行(每个订单)将是空白的。重新启动程序,它们可能会重新出现。这是断断续续的。我已经确认通过日志&调试:
private ObservableCollection<OrderLine> LoadOrderLines()
{
Log.Debug("Loading {0} order lines...", this.Model.OrderLines.Count);
var result = new ObservableCollection<OrderLine>();
foreach (var orderLine in this.Model.OrderLines)
{
result.Add(orderLine);
}
return result;
}
对于同一个订单,有时会显示"加载0个订单行…",有时会显示"加载4个订单行…"。
当我加载订单列表时,我不能使用Eager Loading,因为我不想在只有少数订单可能被打开时加载所有订单的所有订单行-我需要保持加载速度尽可能快,只加载需要的东西,因此延迟加载。
它不仅发生在Orders对象上,有时也发生在其他子集合上,但效果是完全相同的。
有人知道为什么EF这样做,我能做些什么来修复它吗?这是一个大问题——我的程序中不能有空的订单行,而它们应该在那里!
可能使用或不使用的额外信息:
这是一个WPF MVVM应用程序。与网站共享的数据层使用Repository/Unit of Work模式。在OrdersRepository:
public IEnumerable<Order> GetOrders(DateTime fromDate, DateTime toDate, IEnumerable<string> sources, bool? paidStatus, bool? shippedStatus, bool? cancelledStatus, bool? pendingStatus)
{
if (sources == null)
{
sources = this.context.OrderSources.Select(s => s.SourceId);
}
return
this.context.Orders.Where(
o =>
o.OrderDate >= fromDate
&& o.OrderDate < toDate
&& sources.Contains(o.SourceId)
&& (!paidStatus.HasValue || ((o.ReceiptId != null) == paidStatus.Value))
&& (!shippedStatus.HasValue || ((o.ShippedDate != null) == shippedStatus.Value))
&& (!pendingStatus.HasValue || (o.IsPending == pendingStatus.Value))
&& (!cancelledStatus.HasValue || (o.Cancelled == cancelledStatus.Value))).OrderByDescending(
o => o.OrderDate);
}
OrdersViewModel然后加载订单,为每个订单创建一个orderViewModel,并将它们放入ObservableCollection中:
var ordersList = this.unitOfWork.OrdersRepository.GetOrders(this.filter).ToList();
foreach (var order in ordersList)
{
var viewModel = this.viewModelProvider.GetViewModel<OrderViewModel, Order>(order);
this.orders.Add(viewModel);
}
延迟加载用于在访问导航属性时自动加载相关实体。但是,在这种情况下,您不是自动执行,而是手动执行。
要做到这一点,你可以禁用延迟加载,并使用显式加载,像这样:context.Entry(order).Collection(o => o.orderLines).Load();
(此外,使用此技术,您可以应用过滤器)。
您的延迟加载问题可能是由于DbContext
在给定时间点缓存相关实体,并在稍后重用该缓存而不触及DB,因此它已经过时了。例如,一个DbContext为一个订单找到3个订单行并缓存它们。(db上下文之外的)其他东西向该订单添加了2个额外的新订单行。然后从第一个db上下文中访问订单行,并获得过时的3个订单行,而不是db中的5个订单行。理论上,有几种方法可以重新加载/刷新DbContext上的缓存数据,但是您可能会遇到麻烦。您更愿意使用我上面建议的显式加载。如果你看到了Load方法的文档,你可以读到:
从数据库中加载实体集合。注意,上下文中已经存在的实体不会被数据库中的值覆盖。
然而,最安全的选择始终是处置DbContext
并创建一个新的。