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

jps*_*ook 9 c# architecture asp.net entity-framework

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

public class Repository
{
    private AppDbContext context;

    public Repository()
    {
        context = new AppDbContext();
    }


    public IQueryable<Post> GetPosts()
    {
        return context.Posts;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我的服务层可以根据其他方法(其中,分页等)的需要执行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;
}
Run Code Online (Sandbox Code Playgroud)

这意味着在我的代码隐藏中,如果我想绑定到转发器或其他控件,我必须执行ToList().

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

Lad*_*nka 19

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

一旦你使用了IQueryable你有简单的存储库,它将在大多数情况下工作,但它更可测试,因为定义的查询IQueryable是linq到实体.如果您模拟存储库,它们是单元测试中的linq-to-objects =您不测试您的实际实现.您需要集成测试来测试查询逻辑.

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

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

编辑:

如评论中所述,使用IQueryable有一些副作用.当你暴露时,IQueryable你必须保持你的上下文活着,直到你执行查询 - 除非你调用,或者执行你的查询的其他函数仍然需要你的上下文存活,否则IQueryable使用延迟执行.IEnumerableToListFirst

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