我的实体框架存储库和服务层方法应该返回哪些类型:List、IEnumerable、IQueryable? [英] Which types should my Entity Framework repository and service layer methods return: List, IEnumerable, IQueryable?

查看:21
本文介绍了我的实体框架存储库和服务层方法应该返回哪些类型:List、IEnumerable、IQueryable?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a concrete repository implementation that returns a IQueryable of the entity:

public class Repository
{
    private AppDbContext context;

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


    public IQueryable<Post> GetPosts()
    {
        return context.Posts;
    }
}

然后我的服务层可以根据需要为其他方法(where、paging 等)执行 LINQ

My service layer can then perform LINQ as needed for other methods (where, paging, etc)

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

Right now my service layer is setup to return 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().

This means in my codebehind I have to do a ToList() if I want to bind to a repeater or other control.

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

Is this the best way to handle the return types or do I need to be converting to list before I return from my service layer methods?

推荐答案

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

Both approaches are possible and it is only matter of choice.

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

Once you use IQueryable you have simple repository which will work in the most cases but it is worse testable because queries defined on IQueryable are linq-to-entities. If you mock repository they are linq-to-objects in unit tests = you don't test your real implementation. You need integration tests to test your query logic.

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

Once you use IEnumerable you will have very complex public interfaces of your repositories - you will need special repository type for every entity which needs special query exposed on the repository. This kind of repositories was more common with stored procedures - each method on the repository was mapped to single stored procedure. This type of repository provides better separation of concerns and less leaky abstraction but in the same time it removes a lot of ORM and Linq flexibility.

对于最后一种方法,您可以采用组合方法,其中方法为最常见的场景(查询使用频率更高)返回 IEnumerable,另一种方法为罕见或复杂的动态构建查询公开 IQueryable.

For the last you can have combined approach where you have methods returning IEnumerable for most common scenarios (queries used more often) and one method exposing IQueryable for rare or complex dynamically build queries.

正如评论中所指出的,使用 IQueryable 有一些副作用.当您公开 IQueryable 时,您必须保持上下文处于活动状态,直到您执行查询 - IQueryable 以与 IEnumerable 相同的方式使用延迟执行,因此除非您调用 ToListFirst 或其他执行查询的函数,您仍然需要您的上下文.

As noted in comments using IQueryable has some side effects. When you expose IQueryable you must keep your context alive until you execute the query - IQueryable uses deferred execution in the same way as IEnumerable so unless you call ToList, First or other functions executing your query you still need your context alive.

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

The simplest way to achieve that is using disposable pattern in the repository - create context in its constructor and dispose it when repository disposes. Then you can use using blocks and execute queries inside them. This approach is for very simple scenarios where you are happy with single context per repository. More complex (and common) scenarios require context to be shared among multiple repositories. In such case you can use something like context provider / factory (disposable) and inject the factory to repository constructor (or allow provider to create repositories). This leads to DAL layer factory and custom unit of work.

这篇关于我的实体框架存储库和服务层方法应该返回哪些类型:List、IEnumerable、IQueryable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆