I’m working on adding an HtmlHelper for pagination, but I am unsure where the proper and/or most beneficial place to put certain parts of the pagination code from a performance and maintainability standpoint.
I am unsure if the Skip(), Take() and Count() portions of Linq to SQL data manipulation should live within the repository or the controller.
I am also unsure if their order and where they are used affects performance in any way.
If they live within the repository from my understanding this is how it would work:
1. I would pass the pageIndex and pageSize as arguments to the repository’s method that grabs the data from the database.
2. Then grab the full data set from the database.
3. Then store the count of TotalItems of that full data set in a variable.
4. Then apply the Skip() and Take() so the data set retains only the page I need.
5. Display the partial data set as a single page in the view.
If they live in the controller from my understanding this is how it would work:
1. I would grab the full data set from the repository and store it into a variable inside of the controller.
2. Then get the count of TotalItems for the full data set.
3. Then apply the Skip() and Take() so the data set retains only the page I need.
4. Display the partial data set as a single page in the view.
Inside the controller (I realize I will incorrectly get the page count here and not TotalItems):
Character[] charactersToShow = charactersRepository.GetCharactersByRank(this.PageIndex, this.PageSize);
RankViewModel viewModel = new RankViewModel
{
Characters = charactersToShow,
PaginationInfo = new PaginationInfo
{
CurrentPage = this.PageIndex,
ItemsPerPage = this.PageSize,
TotalItems = charactersToShow.Count()
}
};
Inside the repository:
public Character[] GetCharactersByRank(int PageIndex, int PageSize)
{
IQueryable characters = (from c in db.Characters
orderby c.Kill descending
select new Character {
CharID = c.CharID,
CharName = c.CharName,
Level = c.Level
});
characters = PageIndex > 1 ? characters.Skip((PageIndex - 1) * PageSize).Take(PageSize) : characters.Take(PageSize);
return characters.ToArray();
}
This code is a partial example of how I was implementing the Skip(), Take() and Count() code living in the repository. I didn’t actually implement getting and returning the TotalItems because that was when I realized I didn’t know the proper place to put this.
Part of the reason I am unsure where to put these is that I don’t know how Linq to SQL works underneath the hood, and thus I don’t know how to optimize for performance. Nor do I know if this is even an issue in this case.
Does it have to grab ALL the records from the database when you do a .Count() on the Linq to SQL?
Does it have to make separate queries if I do a .Count(), then later do a .Skip() and .Take()?
Is there any possible performance problems with using .Count() prior to a .Skip() and .Take()?
This is my first time using an ORM so I’m not sure what to expect. I know I can view the queries Linq to SQL is running, however I feel that listening to someone with experience in this case would be better use of my time.
I would like to understand this more in depth, any insight would be appreciated.
I found this on the NerdDinner site that Marko mentioned above and it answered a lot of my questions.
From NerdDinner on the bottom of page 8: