Paginacja w asp.net mvc

0

Czołem,
Chcę zrobić paginację wyświetlanych rekordów, lecz nie wiem jak rozwiązać pewną rzecz.
Otóż na jednej stronie wyświetlam pewną daną liczbę elementów, więc na logikę powinienem pobrać z bazy tą określoną liczbę rekordów. Lecz pobranie tylko tych danych, które chcę wyświetlać sprawia, że nie mam informacji odnośnie wszystkich dostępnych rekordów (co wpływa na ilość dostępnych stron listy).

Czy powinienem pobrać wszystkie rekordy i wtedy odpowiednio policzyć liczbę stron czy rozwiązać to jeszcze na jakiś inny sposób?

private IQueryable<Category> GetCategories(string name)
{
     IQueryable<Category> categories;

     if (!string.IsNullOrEmpty(name))
         categories = context.Categories.Where(x => x.Name.ToLower().Contains(name.ToLower()));
     else
         categories = context.Categories;

    return categories;
}
public CategoryListViewModel GetCategoryListViewModel(string name, int? page, int pageSize)
{
       var categories = GetCategories(name);
       var pageNumber = page ?? 1;

       var viewModel = new CategoryListViewModel()
       {
           Name = name,
           Page = pageNumber,
           Categories = categories.OrderBy(x => x.Id).Skip(pageSize * (pageNumber - 1)).Take(pageSize).ProjectTo<CategoryListItemViewModel().ToList(),
           TotalPages = GetTotalPages(categories, pageSize)
       };

       return viewModel;
}
private int GetTotalPages(IEnumerable<Category> categories, int pageSize)
{
    var quantity = categories.Count();
    return Convert.ToInt32(Math.Ceiling((decimal)quantity / (decimal)pageSize));
}
2

Jeśli odpalisz Count() na IQueryable to zostanie to przetłumaczone na sqlowy count i dostaniesz liczbę rekordów bez pobierania wszystkich.

0
mad_penguin napisał(a):

Jeśli odpalisz Count() na IQueryable to zostanie to przetłumaczone na sqlowy count i dostaniesz liczbę rekordów bez pobierania wszystkich.

Myślisz, że odpowiednim byłoby, aby metoda GetCategories zwracała Tuple, zawierając listę rekordów do wyświetlenia (w zależności od tego ile chcemy wyświetlić na stronie) oraz liczbę wszystkich dostępnych rekordów?

1

Jeśli to prywatna metoda to ujdzie. Dla publicznej stworzyłbym osobną klasę, aby nie było wątpliwości, czym są poszczególne wartości.

0

Sam wczoraj się z tym bawiłem i stworzyłem sobie klasę pomocniczą do obsługi paginacji. W konstruktor wrzucam interesujące mnie dane z bazy danych.


    public class PagenationInfo<T>
    {
        public List<T> PageElements { get; set; }

        public int SelectedPage { get; set; }
        public int TotalPage { get; set; }
        public int PageSize { get; set; }

        public PagenationInfo()
        {

        }

        public PagenationInfo(List<T> pageElements, int pageSize){
            this.PageElements = pageElements;

            this.PageSize = pageSize;
            
            double calc = PageElements.Count / (double)pageSize;
            this.TotalPage = (int)Math.Ceiling(calc);
        }

        public List<T> GetSelectedPageElements()
        {
            var itemCount = PageElements.Count;
            int firstElement = (PageSize * SelectedPage) - PageSize;

            List<T> Elements = PageElements;

            if (itemCount > 0)
            {
                if ((double)PageElements.Count % (double)PageSize != 0 && SelectedPage == TotalPage)
                {
                    Elements = PageElements.GetRange(firstElement, ((PageElements.Count) - firstElement));
                }
                else
                {
                    Elements = PageElements.GetRange(firstElement, PageSize);
                }
            }

            return Elements;
        }

        public List<T> GetSelectedPageElements(int pageToSelect)
        {
            if (pageToSelect <= TotalPage)
                this.SelectedPage = pageToSelect;

            return GetSelectedPageElements();
        }

        public List<T> GetSelectedPageElements(int pageToSelect, int pageSize)
        {
            PageSize = pageSize;
            double calc = PageElements.Count / (double)pageSize;
            TotalPage = (int)Math.Ceiling(calc);

            if (pageToSelect <= TotalPage)
                this.SelectedPage = pageToSelect;
            else
                this.SelectedPage = 1;

            return GetSelectedPageElements();
        }
    }
0

Czy taki fragment kody filtrujący dane jest poprawny? Czy póki operuję na IQueryable to operacje wykonują się po stronie bazy, czy po wykonaniu już pierwszego zapytania pobieram dane i dalej operuję już jak na zwykłej liście IEnumerable?


            IQueryable<Product> products = context.Products.AsQueryable();

            if (!String.IsNullOrEmpty(name))
                products = products.Where(p => p.Name.ToLower().Contains(name.ToLower()));
            if (manufacutrerId != 0)
                products = products.Where(p => p.Manufacturer.Id == manufacutrerId);
            if (priceFrom != null)
                products = products.Where(p => p.Price >= priceFrom);
            if (priceTo != null)
                products = products.Where(p => p.Price <= priceTo);

0

Tak, jest poprawny.

0

@mad_penguin: ok, dzięki. A mógłbyś się jeszcze odnieść do mojej teorii odnośnie IQueryable?

0

Tak, zapytanie do bazy leci dopiero po wywołaniu czegoś, co zwraca te dane, ToList, First itp, wcześniej są tylko składane operacje, które będą wykonane ;)
Przy czym zwracam uwagę na EF Core, jeśli użyjesz np. we Where czegoś, czego EF nie potrafi przetłumaczyć na SQL, to on ściągnie wszystkie dane z bazy i zrobi filtrowanie w aplikacji, więc można się nadziać ;) Natomiast EF6 rzuci wyjątek.

1

Dopóki nie wywołasz ToList, First*, Single* albo Select, to nic z bazy nie jest pobierane. W przeciwnym razie istnienie IQueryable byłoby całkowicie i kompletnie pozbawione sensu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1