我应该从我的DAL中返回IEnumerable <T>或IQueryable <T>吗?
我知道这可能是意见,但我正在寻找最佳实践。
据我所知, IQueryable<T>
实现IEnumerable<T>
,所以在我的DAL中,我目前有方法签名,如下所示:
IEnumerable<Product> GetProducts(); IEnumerable<Product> GetProductsByCategory(int cateogoryId); Product GetProduct(int productId);
我应该在这里使用IQueryable<T>
吗?
这两种方法的优缺点是什么?
请注意,我正在计划使用存储库模式,所以我将有一个这样的类:
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>
吗? 这些或那个有什么优点/缺点?
这取决于你想要什么样的行为。
- 返回一个IList <T>告诉调用者他们已经收到了他们所请求的所有数据
- 返回一个IEnumerable <T>告诉调用者他们需要迭代结果,并且可能会被延迟加载。
- 返回一个IQueryable <T>告诉调用者结果是由一个可以处理某些类查询的Linq提供者支持的,从而把调用者的负担形成一个高性能的查询。
虽然后者给予调用者很大的灵活性(假设你的存储库完全支持它),但它是最难testing的,可以说是最不确定的。
还有一件事要考虑:你的分页/sorting支持在哪里? 如果您在存储库中提供分页支持,则返回IEnumerable<T>
。 如果你是在你的版本库之外进行分页(比如在控制器或者服务层中),那么你真的想使用IQueryable<T>
因为你不想在分页之前把整个数据集加载到内存中。
HUUUUGGGE区别。 我看到这一点。
你创build一个IQueryable之前,它击中数据库。 IQueryable只有在调用一个渴望的函数(例如.ToList())时才会触发数据库,或者实际上尝试将值拉出。 IQueryable =懒惰。
一个IEnumerable将立即执行你的lambda对数据库。 IEnumerable =渴望。
至于与Repository模式使用哪一个,我相信它是渴望的。 我通常会看到IList通过,但其他人需要为你解决这个问题。 编辑 – 你通常看到IEnumerable,而不是IQueryable,因为你不希望层通过你的知识库A)确定何时会发生数据库命中或B)添加任何逻辑到仓库外的联接
有一个非常好的LINQvideo,我喜欢很多 – 它不仅仅是IEnumerable v IQueryable,但它确实有一些非常好的洞察力。
http://channel9.msdn.com/posts/matthijs/LINQ-Tips-Tricks-and-Optimizations-by-Scott-Allen/
您可以使用IQueryable,并接受某人可以创build一个SELECT N + 1可能发生的场景。 这是一个缺点,同时还有一个事实,即最终可能会在代码库上方的代码中实现特定于您的存储库实现的代码。 这样做的好处是,你可以让分配和sorting等委托常见操作在你的存储库之外表示,从而减轻这种担忧。 如果您需要将数据连接到其他数据库表,那么它也更加灵活,因为查询将保持为一个expression式,因此可以在parsing到查询并添加到数据库之前添加到该expression式中。
另一种方法是locking您的存储库,以便通过调用ToList()
来返回物化列表。 通过分页和sorting的例子,您需要传入skip,take和一个sortingexpression式作为参数到您的仓库的方法,并使用参数返回一个结果窗口。 这意味着存储库正在承担分页和sorting的责任,以及数据的所有投影。
这是一个判断呼吁,你是否给你的应用程序的linq的权力,并具有较less的复杂性在存储库,或者你控制你的数据访问。 对我来说,这取决于与每个实体相关联的查询的数量,以及实体的组合,以及我想在哪里pipe理这种复杂性。