我有一个具体的存储库实现,它返回实体的IQueryable:
public class Repository
{
private AppDbContext context;
public Repository()
{
context = new AppDbContext();
}
public IQueryable<Post> GetPosts()
{
return context.Posts;
}
}
我的服务层可以根据需要为其他方法(其中,分页等)执行LINQ
现在我的服务层设置为返回IEnumerable:
public IEnumerable<Post> GetPageOfPosts(int pageNumber, int pageSize)
{
Repository postRepo = new Repository();
var posts = (from p in postRepo.GetPosts() //this is IQueryable
orderby p.PostDate descending
select p)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
return posts;
}
这意味着在我的代码隐藏中,如果我想绑定到转发器或其他控件,我必须执行ToList()。
这是处理返回类型的最佳方法,还是在从服务层方法返回之前需要转换为列表?
答案 0 :(得分:19)
这两种方法都是可能的,这只是选择的问题。
一旦你使用IQueryable
,你就拥有了一个简单的存储库,它可以在大多数情况下工作,但它更可测试,因为在IQueryable
上定义的查询是linq-to-entities。如果您模拟存储库,它们是单元测试中的linq-to-objects =您不测试您的实际实现。您需要集成测试来测试查询逻辑。
使用IEnumerable
后,您的存储库将拥有非常复杂的公共接口 - 对于需要在存储库中公开特殊查询的每个实体,您都需要特殊的存储库类型。这种存储库在存储过程中更常见 - 存储库中的每个方法都映射到单个存储过程。这种类型的存储库提供了更好的关注点分离和更少的漏洞抽象,但同时它消除了大量的ORM和Linq灵活性。
对于最后一个,您可以使用组合方法,其中有大多数常见方案返回IEnumerable的方法(更常用的查询)和一个方法,用于为罕见或复杂的动态构建查询公开IQueryable
。
编辑:
正如使用IQueryable
的评论中所述,有一些副作用。展示IQueryable
时,必须保持上下文的活动状态,直到执行查询为止 - IQueryable
使用延迟执行的方式与IEnumerable
相同,除非您调用ToList
,{{ 1}}或执行查询的其他函数仍需要上下文。
实现这一目标的最简单方法是在存储库中使用一次性模式 - 在其构造函数中创建上下文,并在存储库处置时对其进行处理。然后,您可以使用First
块并在其中执行查询。此方法适用于非常简单的场景,您可以对每个存储库的单个上下文感到满意。更复杂(和常见)的场景需要在多个存储库之间共享上下文。在这种情况下,您可以使用上下文提供程序/工厂(一次性)之类的东西,并将工厂注入存储库构造函数(或允许提供程序创建存储库)。这导致了DAL层工厂和定制工作单元。
答案 1 :(得分:3)
您的问题的另一个词似乎需要确定何时处置AppDbContext
或在何处处置。
如果你没有处理它,意味着它在应用程序退出时被处理掉,那么返回IEnumerable / IQueryable是没有问题的,没有实际数据。但是,在AppDbContext
处置之前,您需要将类型作为IList返回,并具有实际数据。
<强>更新强> 我认为你需要抓住以下代码,尽管你已经知道了。
//outside of this code is refered to your code.
//Returning IEnumerable could be used outside this scope if AppDbContext is ensured no disposing
public IEnumerable<Post> GetIEnumerableWithoutActualData()
{
return context.Posts;
}
//Even if AppDbContext is disposed, IEnumerable could be used.
public IEnumerable<Post> GetIEnumerableWithActualData()
{
return context.Posts.ToList();
}
答案 2 :(得分:0)
您的返回类型应始终尽可能高于继承层次结构(或者我应该将其写为低,如果基础是底部) 。如果您的所有方法都需要IQueryable<T>
,那么所有返回值都应该放弃该类型。
也就是说,IEnumerable<T>
有一个方法(AsQueryable()
),你可以调用它来实现(我相信的)理想的结果。