Entity Framework的Code-First IQueryable导航属性

6
我在网上看到的大部分示例都将导航属性显示为ICollection或直接使用List实现。它们通常是virtual的,以启用延迟加载。
然而,当您访问这种属性时,它会在内存中加载整个集合,如果您在其后有一个子查询(即object.MyListProperty.Where(...)),我注意到每个MyListProperty中的项目都会发出一个SQL查询。
如何避免这种情况?我希望list属性后面的where子句能够在SQL服务器上执行,如果可能的话。我可以使用IQueryable导航属性吗?是否有任何最佳实践适用于这种情况?

2
可能是Entity Framework:导航属性问题的重复。 - chambo
1
我理解你的观点,但是 IQueryable<T> 不是一个有效的导航属性类型。即使是 IEnumerable<T> 也不行。ICollection<T> 是最低要求,当然还可以从它派生。 - Ivan Stoev
1个回答

1
我的建议是为了最佳实践,应该完全禁用惰性加载。相反,强制调用者通过包含语句或使用投影来急切地加载导航属性。
引用: 有第三方产品支持带过滤器的包含操作,如本文所述:如何在实体框架中过滤包含的实体,但根据我的经验,这会进一步复杂化检索到的对象的下游处理。如果实体对象在方法X之外被加载,因为方法X无法确定导航属性是否已经使用正确的过滤器加载,方法X将从重新查询它知道自己需要的精确行开始。
using (var context = new MyDbContext())
{
    context.Configuration.LazyLoadingEnabled = false;

    // TODO: load your data
    ...
}

以这种方式,只有在显式请求时才会加载记录。
当您想要访问 IQueryable 以便推迟数据的加载时,请从 DbContext 实例 而不是从对象中进行查询。
  • 在此示例中假设一个 Customer 有成千上万的交易记录,因此我们不希望它们被 急切地懒惰地 加载。
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 访问它们,而不是从之前检索到的实例中访问。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接