我的Entity Framework存储库和服务层方法应返回哪些类型:List,IEnumerable,IQueryable?

我有一个具体的存储库实现,它返回实体的IQueryable:

public class Repository { private AppDbContext context; public Repository() { context = new AppDbContext(); } public IQueryable GetPosts() { return context.Posts; } } 

然后,我的服务层可以根据其他方法(其中,分页等)的需要执行LINQ

现在我的服务层设置为返回IEnumerable:

 public IEnumerable 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()。

这是处理返回类型的最佳方法,还是在从服务层方法返回之前需要转换为列表?

这两种方法都是可能的,这只是选择的问题。

一旦你使用了IQueryable你就拥有了一个简单的存储库,它可以在大多数情况下工作,但它更容易测试,因为在IQueryable上定义的查询是linq-to-entities。 如果您模拟存储库,它们是unit testing中的linq-to-objects =您不测试您的实际实现。 您需要集成测试来测试查询逻辑。

一旦使用IEnumerable您将拥有非常复杂的存储库公共接口 – 对于需要在存储库上公开特殊查询的每个实体,您将需要特殊的存储库类型。 这种存储库在存储过程中更常见 – 存储库中的每个方法都映射到单个存储过程。 这种类型的存储库提供了更好的关注点分离和更少的漏洞抽象,但同时它消除了大量的ORM和Linq灵活性。

对于最后一个,您可以使用组合方法,其中您有方法返回IEnumerable以用于大多数常见方案(更常用的查询)和一个方法,以便为罕见或复杂的动态构建查询公开IQueryable

编辑:

正如使用IQueryable评论中所述,有一些副作用。 当你暴露IQueryable你必须保持你的上下文活着,直到你执行查询 – IQueryable以与IEnumerable相同的方式使用延迟执行,所以除非你调用ToListFirst或其他执行查询的函数,你仍然需要你的上下文存活。

实现这一目标的最简单方法是在存储库中使用一次性模式 – 在其构造函数中创建上下文,并在存储库配置时对其进行处理。 然后,您可以使用块并在其中执行查询。 此方法适用于非常简单的场景,您可以对每个存储库的单个上下文感到满意。 更复杂(和常见)的场景需要在多个存储库之间共享上下文。 在这种情况下,您可以使用上下文提供程序/工厂(一次性)之类的东西,并将工厂注入存储库构造函数(或允许提供程序创建存储库)。 这导致DAL层工厂和定制工作单元。

您的问题的另一个词似乎需要确定何时处置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 GetIEnumerableWithoutActualData() { return context.Posts; } //Even if AppDbContext is disposed, IEnumerable could be used. public IEnumerable GetIEnumerableWithActualData() { return context.Posts.ToList(); } 

您的返回类型应始终在inheritance层次结构中尽可能高( 或者如果基础位于底部,我应该将其写为 )。 如果您的所有方法都需要IQueryable ,那么所有返回值都应该放弃该类型。

也就是说, IEnumerable有一个方法( AsQueryable() ),你可以调用它来实现(我相信)所需的结果。