9

I am using EF4 with code first and have a repository for persistence and a service layer that interacts with it. I have a service layer method that calls a IQueryable method on my repository and returns a IEnumerable containing the entities. I also need to return the total record count so I can calculate the paging links.

How should I return both the int and IEnumerable from my service method?

  • Use a out parameter on the method for the total row count
  • Create a separate class that includes the total row count as a property
  • Move the paging LINQ query out of the service layer (expose the IQueryable from the repo on the service layer)
  • Create a full separate method on the service layer that does a new query just for count.

All of these should work, but which one is the cleanest?

UPDATE: Here is some clarification of the architecture. If this is wrong, then please tell me better way (eg - do the paging in the presentation layer instead of service layer,etc)

Repo layer:

returns IQueryable of DbSet, abstracts the db access from the presentation layer

Service layer:

does a LINQ query on the IQueryable to filter and just get the page items as needed using skip and take and returns a IEnumerable (going to also set to List on return to avoid any DbContext lifetime issues)

Presentation layer:

Call the method on the Service layer (getPagedResults(filters, pageNumber, pageSize))

From the looks of it I will also need to add a separate method to get the total results. Was hopeing to do this all in one call.

I would prefer not to bring back all the records to presentation and then page... seems inefficient.

jpshook
  • 4,834
  • 6
  • 36
  • 45
  • If you're already returning `IEnumerable` is there some specific reason you don't just use `.Count()` on the `IEnumerable`? Are you just presenting the subset to the front end? Shouldn't the front end be responsible for paging? – AllenG Jun 20 '11 at 21:42
  • The IEnumerable you return, does it contain all the items? (same nr. of items as count would return) – Magnus Jun 20 '11 at 21:44
  • @AllenG - I was doing the paging in the service layer itself, then returning a list to the UI/Codebehind consumer. This way I can avoid any issues with my Dbcontext lifetime. Also, then I wouldn't have to send all the records to front end, then do the paging.. would directly fetch the records I need in the service layer query. – jpshook Jun 21 '11 at 14:32
  • @Magnus - I was trying to avoid sending all the items... Just take the ones I need via a query against the IQueryable returned from the repo. – jpshook Jun 21 '11 at 14:33
  • Updated question to add more info. – jpshook Jun 21 '11 at 14:40

2 Answers2

10

You can do something like this

public class Repository<TEntity>
{
   public IEnumerable<TEntity> GetCollection(Expression<Func<TEntity, bool>> filter, 
      int pageSize, int pageIndex)
   {
      return YourDbSet.Where(filter).OrderBy(sortExpression).Skip(pageSize * pageIndex).Take(pageSize);
   }

   public int Count(Expression<Func<TEntity, bool>> filter)
   {
      return YourDbSet.Where(filter).Count();
   }
}

Then You can write an extension method to use both of these methods

public static Pagination<TEntity> GetPagination<TEntity>(this Repository<TEntity> repository, 
   Expression<Func<TEntity, bool>> filter, int pageSize, int pageIndex)
{
   var entities = repository.GetCollection(filter, pageSize, pageIndex);
   var count = repository.Count(filter);

   return new Pagination(entities, pageSize, pageIndex + 1, count);
}

This way you can reuse GetCollection and Count methods independently. You can build the where condition dynamically. Take a look at my answer

Community
  • 1
  • 1
Eranga
  • 32,181
  • 5
  • 97
  • 96
  • I am still learning, so trying to stick to concrete implementations until I get my head around generics. – jpshook Jun 21 '11 at 14:42
  • @Developr since you are using IQueryable you are already using generics. the generic parameters are inferred by the complier. so you don't have to give them – Eranga Jun 21 '11 at 15:17
  • here is a link to another question that shows my architecture... very simple and concrete: http://stackoverflow.com/questions/6416506/which-types-should-my-entity-framework-repository-and-service-layer-methods-retur – jpshook Jun 21 '11 at 16:39
1

If the Enumerable you are returning contains all the items, I would do a ToList() on it before returning if from the function. (you can then do Count with no cost on it) If the function is returning a sub set of the total (using Skip and take) I would add a seperate function to get the total count.

Magnus
  • 45,362
  • 8
  • 80
  • 118