实体框架代码优先IQueryable导航属性

时间:2017-01-06 16:36:53

标签: c# entity-framework ef-code-first iqueryable

我在互联网上看到的大多数示例都将导航属性显示为ICollection或直接List实现。它们通常为virtual,以启用延迟加载。

但是,当您访问此类属性时,它会将整个集合加载到内存中,如果您之后有子查询(即object.MyListProperty.Where(...)),我注意到将为每个项目发出一个SQL查询MyListProperty

我该如何避免这种情况?我希望list属性之后的where子句在SQL服务器上执行,如果可能的话。我可以使用IQueryable导航属性吗?这种情况有没有最好的做法?

1 个答案:

答案 0 :(得分:1)

我对最佳实践的建议是完全禁用 延迟加载。而是强制调用者通过include语句或使用投影急切地加载导航属性。

有一些支持包含过滤器的第三方产品,如这篇文章How to filter include entities in entity framework中所述,但是以我的经验,这进一步使所检索对象的下游处理更加复杂。如果将实体对象加载到方法X之外,则由于方法X无法确定是否已使用正确的过滤器加载导航属性,因此方法X将重新启动-查询所需的确切行。

using (var context = new MyDbContext())
{
    context.Configuration.LazyLoadingEnabled = false;

    // TODO: load your data
    ...
}

通过这种方式,仅在明确请求记录时才加载记录。

当您想访问IQueryable以便推迟数据加载时,然后针对DbContext instance (而不是来自对象)进行查询。

  • 在此示例中,假设Customer具有数千个事务,因此我们根本不希望它们被急切地 lazy 加载。
using (var context = new MyDbContext())
{
    context.Configuration.LazyLoadingEnabled = false;

    var customer = context.Customers.First(x => x.Id == 123);
    ...
    // count the transactions in the last 30 days for this customer
    int customerId = customer.Id;  
    DateTime dateFrom = DateTime.Today.AddDays(-30)

    // different variations on the same query
    int transactionCount1 = context.Customers.Where(x => x.Id == customerId)
                                             .SelectMany(x => x.Transactions.Where(x => x.TransactionDate >= dateFrom))
                                             .Count();
    int transactionCount2 = context.Customers.Where(x => x.Id == customerId)
                                             .SelectMany(x => x.Transactions)
                                             .Where(x => x.TransactionDate >= dateFrom)
                                             .Count();
    int transactionCount3 = context.Transactions.Where(x => x.CustomerId == customerId)
                                                .Where(x => x.TransactionDate >= dateFrom)
                                                .Count();
}

您已经确定要使用IQueryable<T>很好,我们可以直接从DbContext访问它们,而不是从先前检索到的实例中访问它们。