我知道这可能是意见,但我正在寻找最佳实践。
据我了解,IQueryable<T>
实现了IEnumerable<T>
,所以在我的DAL中,我目前有方法签名,如下所示:
IEnumerable<Product> GetProducts();
IEnumerable<Product> GetProductsByCategory(int cateogoryId);
Product GetProduct(int productId);
我应该在这里使用IQueryable<T>
吗?
两种方法的优点和缺点是什么?
请注意,我打算使用Repository模式,所以我将有一个类似的类:
public class ProductRepository {
DBDataContext db = new DBDataContext(<!-- connection string -->);
public IEnumerable<Product> GetProductsNew(int daysOld) {
return db.GetProducts()
.Where(p => p.AddedDateTime > DateTime.Now.AddDays(-daysOld ));
}
}
我应该将IEnumerable<T>
更改为IQueryable<T>
吗?一个或另一个有哪些优点/缺点?
答案 0 :(得分:52)
这取决于你想要的行为。
虽然后者为调用者提供了很大的灵活性(假设您的存储库完全支持它),但它是最难测试的,并且可以说是最不确定的。
答案 1 :(得分:7)
还有一件事需要考虑:您的分页/排序支持在哪里?如果您在存储库中提供分页支持,则返回IEnumerable<T>
就可以了。如果您在存储库之外进行分页(例如在控制器或服务层中),那么您确实想要使用IQueryable<T>
,因为您不希望在分页之前将整个数据集加载到内存中。
答案 2 :(得分:5)
HUUUUGGGE的区别。我看到了这一点。
在您访问数据库之前构建IQueryable。一旦调用了一个eager函数(例如.ToList()),IQueryable只会命中DB,或者你实际上试图将值拉出来。 IQueryable =懒惰。
IEnumerable将立即对数据库执行lambda。 IEnumerable =渴望。
至于使用Repository模式,我相信它很渴望。我经常看到IList被传递,但其他人需要为你解决这个问题。编辑 - 您通常会看到IEnumerable而不是IQueryable,因为您不希望图层超过您的存储库A)确定何时会发生数据库命中或B)向存储库外的连接添加任何逻辑
有一个非常好的LINQ视频,我很喜欢它 - 它不仅仅是IEnumerable v IQueryable,它确实有一些很棒的洞察力。
http://channel9.msdn.com/posts/matthijs/LINQ-Tips-Tricks-and-Optimizations-by-Scott-Allen/
答案 3 :(得分:3)
您可以使用IQueryable并接受某人可以创建可能发生SELECT N+1的方案。这是一个缺点,同时您最终可能会在存储库上方的层中获得特定于存储库实现的代码。这样做的好处是,您允许在您的存储库之外表达委派常见操作(如分页和排序),从而减轻此类问题。如果您需要将数据与其他数据库表连接,它也会更灵活,因为查询将保留为表达式,因此可以在将其解析为查询并点击数据库之前添加。
另一种方法是锁定存储库,以便通过调用ToList()
返回实体化列表。使用分页和排序的示例,您需要将skip,take和sort表达式作为参数传递给存储库的方法,并使用这些参数仅返回结果窗口。这意味着存储库负责分页和排序,以及数据的所有投影。
这是一个判断调用,是否为应用程序提供了linq的强大功能,并且存储库中的复杂性较低,或者您是否控制了数据访问。对我来说,这取决于与每个实体相关联的查询数量,实体组合以及我想要管理这种复杂性的位置。