ASP.NET MVC2 LINQ - Шаблон репозитория, куда должен идти код нумерации страниц? - PullRequest
3 голосов
/ 02 ноября 2010

Я работаю над добавлением HtmlHelper для разбивки на страницы, но я не уверен, где правильное и / или наиболее выгодное место для размещения определенных частей кода разбивки на страницы с точки зрения производительности и удобства обслуживания.

Я не уверен, должны ли части Skip (), Take () и Count () обработки данных Linq to SQL находиться в репозитории или контроллере.

Я также не уверен, влияет ли их порядок и место их использования на производительность.

Если они живут в хранилище, насколько я понимаю, вот как это будет работать:
1. Я бы передал pageIndex и pageSize в качестве аргументов методу репозитория, который получает данные из базы данных.
2. Затем получите полный набор данных из базы данных.
3. Затем сохраните количество TotalItems этого полного набора данных в переменной.
4. Затем примените Skip () и Take (), чтобы набор данных сохранил только нужную мне страницу.
5. Отображение набора частичных данных в виде одной страницы в представлении.

Если они живут в контроллере из моего понимания, вот как это будет работать: 1. Я бы взял полный набор данных из репозитория и сохранил его в переменной внутри контроллера. 2. Затем получите количество TotalItems для полного набора данных. 3. Затем примените Skip () и Take (), чтобы набор данных сохранил только нужную мне страницу. 4. Отобразите частичные данные, заданные в виде одной страницы, в представлении.

Внутри контроллера (я понимаю, что неправильно получу количество страниц здесь, а не 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()
    }
};

Внутри хранилища:


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

Этот код является частичным примером того, как я реализовывал код Skip (), Take () и Count (), живущий в хранилище. На самом деле я не реализовывал получение и возврат TotalItems, потому что именно тогда я понял, что не знаю, где это поставить.

Одна из причин, по которой я не уверен, куда их поместить, заключается в том, что я не знаю, как работает Linq to SQL, и поэтому не знаю, как оптимизировать производительность. Также я не знаю, является ли это проблемой в данном случае.

Должен ли он получать ВСЕ записи из базы данных, когда вы выполняете .Count () в Linq to SQL? Нужно ли делать отдельные запросы, если я делаю .Count (), а потом делаю .Skip () и .Take ()? Есть ли возможные проблемы с производительностью при использовании .Count () до .Skip () и .Take ()?

Я впервые использую ORM, поэтому я не уверен, чего ожидать. Я знаю, что могу просматривать запросы, которые выполняет Linq to SQL, однако я чувствую, что прослушивание кого-либо, имеющего опыт в этом случае, будет лучше использовать мое время.

Я хотел бы понять это более глубоко, любая оценка будет оценена.

Ответы [ 2 ]

2 голосов
/ 03 ноября 2010

Я нашел это на сайте NerdDinner , о котором Марко упоминал выше, и он ответил на многие мои вопросы.

С NerdDinner внизу страницы 8:

IQueryable - это очень мощная функция, которая позволяет использовать множество интересных сценариев отложенного выполнения (таких как запросы на пейджинг и на основе композиции). Как и со всеми мощными функциями, вы должны быть осторожны с тем, как вы используете его, и убедитесь, что им не злоупотребляют.
Важно понимать, что возврат результата IQueryable из вашего репозитория позволяет вызывающему коду добавлять к нему методы цепочечного оператора и, таким образом, участвовать в окончательном выполнении запроса. Если вы не хотите предоставлять вызывающему коду эту возможность, вы должны вернуть обратно результаты IList или IEnumerable, которые содержат результаты уже выполненного запроса.
Для сценариев разбиения на страницы это потребует от вас вставить действительную логику разбиения данных в вызываемый метод репозитория. В этом сценарии мы можем обновить наш метод поиска FindUpcomingDinners (), чтобы иметь подпись, которая либо возвращала PaginatedList:
PaginatedList FindUpcomingDinners (int pageIndex, int pageSize) {}
Или верните обратно IList и используйте параметр «totalCount», чтобы получить общее количество обедов:
IList FindUpcomingDinners (int pageIndex, int pageSize, out int totalCount) {}
2 голосов
/ 02 ноября 2010

Я храню общий класс PaginatedList в своей папке Helpers, куда я также помещаю другие классы Helper.

PaginatedList находится прямо из NerdDinner и выглядит следующим образом.

public class PaginatedList<T>: List<T>
{
    public int PageIndex { get; private set; }
    public int PageSize { get; private set; }
    public int TotalCount { get; private set; }
    public int TotalPages { get; private set; }

    public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        TotalCount = source.Count();
        TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);

        this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
    }

    public bool HasPreviousPage
    {
        get
        {
            return (PageIndex > 0);
        }
    }

    public bool HasNextPage
    {
        get
        {
            return (PageIndex + 1 < TotalPages);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...