【问题标题】:Entity Framework is sometimes not loading the contents of Child Objects/Collections实体框架有时不加载子对象/集合的内容
【发布时间】:2014-07-21 02:38:47
【问题描述】:

我开始遇到一个问题,即实体框架中的子集合无法通过延迟加载正确加载。

这方面最突出的例子是我的 Orders 对象 - 每个 Order 都有一个或多个与之关联的 Order Lines(即,已订购的产品和数量的列表)。有时,当程序运行时,您可以打开一些订单,并且所有订单行(对于每个订单)都是空白的。重新启动程序,它们可能会重新出现。这很断断续续。

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

    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 应用程序。与网站共享的数据层使用存储库/工作单元模式。

在 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);
        }

【问题讨论】:

  • 这篇文章很长,周六看,不过,也许你必须使用Include的方法。
  • 我想过,但我不想在加载时将它们包含在 每个 订单中,因为我事先不知道哪些会被打开,它必须是延迟加载。
  • 我的回答解决了你的问题吗?

标签: c# .net wpf entity-framework mvvm


【解决方案1】:

延迟加载是在您访问导航属性时自动加载相关实体。但是,在这种情况下,您不是自动执行此操作,而是手动执行。

为此,您可以禁用延迟加载,并使用显式加载,如下所示:

context.Entry(order).Collection(o => o.orderLines).Load();

(此外,使用这种技术,您可以应用过滤器)。

您的延迟加载问题可能是长期存在的DbContext 的结果,该DbContext 在给定时间点缓存相关实体,并在以后重用此缓存,而不会影响数据库,因此它已过时。 IE。一个 DbContext 为一个订单找到 3 个订单行并缓存它们。其他东西(在 db 上下文之外)向该订单添加了 2 个额外的新订单行。然后,您从第一个数据库上下文访问订单行并获取过时的 3 个订单行,而不是数据库中的 5 个。从理论上讲,您可以通过多种方式重新加载/刷新 DbContext 上的缓存数据,但您可能会遇到麻烦。您宁愿使用我上面建议的显式加载。如果您看到docs for Load method,您可以阅读以下内容:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多