Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
356 views
in Technique[技术] by (71.8m points)

c# - Which types should my Entity Framework repository and service layer methods return: List, IEnumerable, 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;
    }
}

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

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;
}

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

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.

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.

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.

Edit:

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.

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...