繁体   English   中英

实体框架有时未加载子对象/集合的内容

[英]Entity Framework is sometimes not loading the contents of Child Objects/Collections

我开始遇到一个问题,即使用延迟加载无法正确加载Entity Framework中的子集合。

最突出的例子是我的Orders对象-每个订单都有一个或多个与其关联的订单行(即,已订购哪些产品以及订购多少产品的列表)。 有时,当程序运行时,您可以打开一些订单,所有订单行(对于每个订单)都为空白。 重新启动该程序,它们可能会重新出现。 这是断断续续的。

我已经通过日志和调试确认子集合中没有条目:

    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条订单行...”。

加载订单列表时,我无法使用“急切加载”,因为当可能只有几个打开时,我不想加载所有订单的所有订单行-我需要保持加载速度与可能,并且仅在需要时加载它们,因此延迟加载。

它不仅发生在Orders对象上,有时还发生在其他子集合上,但效果完全相同。

有人知道EF为什么要这样做以及我可以如何解决吗? 这是一个很大的问题-我的程序中不能有空的订单行!

可能会或可能不会使用的额外信息:

这是WPF MVVM应用程序。 与网站共享的数据层使用存储库/工作单元模式。

在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寿命长的结果,该DbContext在给定的时间点缓存相关实体,然后在不影响数据库的情况下稍后重用此缓存,因此它已经过时了。 即,一个DbContext为一条订单找到3条订单行并将其缓存。 其他(在db上下文之外)向此订单添加了2条额外的新订单行。 然后,您从第一个数据库上下文访问订单行,并获得过时的3条订单行,而不是数据库中的5条。 从理论上讲,有几种方法可以重新加载/刷新DbContext上的缓存数据,但是您可能会遇到麻烦。 您宁愿使用我上面建议的显式加载。 如果您看到有关Load方法文档 ,则可以阅读以下内容:

从数据库加载实体集合。 请注意,上下文中已经存在的实体不会被数据库中的值覆盖。

但是,最安全的选择始终是处置DbContext并创建一个新的DbContext (请阅读上面代码块的第二句话)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM