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?
Both approaches are possible and it is only matter of choice.
Once you use
IQueryableyou have simple repository which will work in the most cases but it is worse testable because queries defined onIQueryableare 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
IEnumerableyou 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
IQueryablefor rare or complex dynamically build queries.Edit:
As noted in comments using
IQueryablehas some side effects. When you exposeIQueryableyou must keep your context alive until you execute the query –IQueryableuses deferred execution in the same way asIEnumerableso unless you callToList,Firstor 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
usingblocks 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.