我在互联网上看到的大多数示例都将导航属性显示为ICollection
或直接List
实现。它们通常为virtual
,以启用延迟加载。
但是,当您访问此类属性时,它会将整个集合加载到内存中,如果您之后有子查询(即object.MyListProperty.Where(...)
),我注意到将为每个项目发出一个SQL查询MyListProperty
。
我该如何避免这种情况?我希望list属性之后的where
子句在SQL服务器上执行,如果可能的话。我可以使用IQueryable
导航属性吗?这种情况有没有最好的做法?
答案 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
访问它们,而不是从先前检索到的实例中访问它们。