Aracılığıyla paylaş


Bölüm 3, Razor ASP.NET Çekirdeğinde olan EF Core sayfalar - Sıralama, Filtreleme, Sayfalama

Tom Dykstra, Jeremy Likness ve Jon P Smith tarafından

Contoso University web uygulaması, ve Visual Studio kullanarak EF Core Sayfalar web uygulamalarının nasıl oluşturulacağını Razor gösterir. Öğretici serisi hakkında bilgi için ilk öğreticiye bakın.

Çözemediğiniz sorunlarla karşılaşırsanız, tamamlanmış uygulamayı indirin ve öğreticiyi izleyerek bu kodu oluşturduğunuz kodla karşılaştırın.

Bu öğretici, Öğrenciler sayfalarına sıralama, filtreleme ve sayfalama işlevleri ekler.

Aşağıdaki çizimde tamamlanmış bir sayfa gösterilmektedir. Sütun başlıkları, sütunu sıralamak için tıklanabilir bağlantılardır. Artan ve azalan sıralama düzeni arasında geçiş yapmak için art arda bir sütun başlığına tıklayın.

Öğrenciler dizin sayfası

Sıralama ekleme

Sıralama eklemek için içindeki Pages/Students/Index.cshtml.cs kodu aşağıdaki kodla değiştirin.

public class IndexModel : PageModel
{
    private readonly SchoolContext _context;
    public IndexModel(SchoolContext context)
    {
        _context = context;
    }

    public string NameSort { get; set; }
    public string DateSort { get; set; }
    public string CurrentFilter { get; set; }
    public string CurrentSort { get; set; }

    public IList<Student> Students { get; set; }

    public async Task OnGetAsync(string sortOrder)
    {
        // using System;
        NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        DateSort = sortOrder == "Date" ? "date_desc" : "Date";

        IQueryable<Student> studentsIQ = from s in _context.Students
                                        select s;

        switch (sortOrder)
        {
            case "name_desc":
                studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
                break;
            case "Date":
                studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
                break;
            case "date_desc":
                studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
                break;
            default:
                studentsIQ = studentsIQ.OrderBy(s => s.LastName);
                break;
        }

        Students = await studentsIQ.AsNoTracking().ToListAsync();
    }
}

Yukarıdaki kod:

  • eklenmesini using System;gerektirir.
  • Sıralama parametrelerini içerecek özellikler ekler.
  • özelliğinin Student adını olarak Studentsdeğiştirir.
  • yöntemindeki OnGetAsync kodu değiştirir.

yöntemi, URL'deki OnGetAsync sorgu dizesinden bir sortOrder parametre alır. URL ve sorgu dizesi, Bağlayıcı Etiketi Yardımcısı tarafından oluşturulur.

sortOrder parametresi veya Name Dateşeklindedir. Azalan sortOrder sırayı belirtmek için isteğe bağlı olarak parametresini _desc takip edilir. Varsayılan sıralama düzeni artan düzendedir.

Öğrenciler bağlantısından Dizin sayfası istendiğinde sorgu dizesi yoktur. Öğrenciler soyadına göre artan düzende görüntülenir. Soyadına göre artan düzen deyimindekidir default switch . Kullanıcı bir sütun başlığı bağlantısına tıkladığında, sorgu dizesi değerinde uygun sortOrder değer sağlanır.

NameSort ve DateSort Sayfa tarafından Razor sütun başlığı köprülerini uygun sorgu dizesi değerleriyle yapılandırmak için kullanılır:

NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";

Kod, C# koşullu işlecini ?: kullanır. işleci ?: bir üçüncül işleçtir ve üç işlenen alır. İlk satır null veya boş olduğunda sortOrder olarak NameSort ayarlandığını name_descbelirtir. Null veya boş değilsesortOrder, NameSort boş bir dizeye ayarlanır.

Bu iki deyim, sayfanın sütun başlığı köprülerini aşağıdaki gibi ayarlamasını sağlar:

Geçerli sıralama düzeni Soyadı Köprüsü Tarih Köprüsü
Soyadı artan azalan düzende ascending
Soyadı azalan ascending ascending
Artan tarih ascending azalan düzende
Azalan tarih ascending ascending

yöntemi, sıralama ölçütü olarak sütunu belirtmek için LINQ to Entities kullanır. Kod switch deyiminden önce bir IQueryable<Student> başlatır ve switch deyiminde değiştirir:

IQueryable<Student> studentsIQ = from s in _context.Students
                                select s;

switch (sortOrder)
{
    case "name_desc":
        studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
        break;
    case "Date":
        studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
        break;
    case "date_desc":
        studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
        break;
    default:
        studentsIQ = studentsIQ.OrderBy(s => s.LastName);
        break;
}

Students = await studentsIQ.AsNoTracking().ToListAsync();

bir IQueryable oluşturulduğunda veya değiştirildiğinde veritabanına hiçbir sorgu gönderilmez. Nesne bir koleksiyona dönüştürülene kadar IQueryable sorgu yürütülemez. IQueryable gibi ToListAsyncbir yöntemi çağırarak bir koleksiyona dönüştürülür. Bu nedenle kod, aşağıdaki deyime IQueryable kadar yürütülmeyen tek bir sorguyla sonuçlanmıştır:

Students = await studentsIQ.AsNoTracking().ToListAsync();

OnGetAsync çok sayıda sıralanabilir sütunla ayrıntılı olabilir. Bu işlevi kod etmenin alternatif bir yolu hakkında bilgi için bkz . Bu öğretici serisinin MVC sürümünde kodu basitleştirmek için dinamik LINQ kullanma.

içindeki Students/Index.cshtmlkodu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Students";
}

<h2>Students</h2>
<p>
    <a asp-page="Create">Create New</a>
</p>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
                    @Html.DisplayNameFor(model => model.Students[0].LastName)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Students[0].FirstMidName)
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
                    @Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
                </a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Students)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.EnrollmentDate)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Yukarıdaki kod:

  • ve EnrollmentDate sütun başlıklarına LastName köprü ekler.
  • geçerli sıralama düzeni değerleriyle köprüler ayarlamak için ve DateSort içindeki bilgileri NameSort kullanır.
  • Sayfa başlığını Dizinden Öğrenciler olarak değiştirir.
  • olarak Model.StudentsdeğiştirilirModel.Student.

Sıralamanın çalıştığını doğrulamak için:

  • Uygulamayı çalıştırın ve Öğrenciler sekmesini seçin.
  • Sütun başlıklarına tıklayın.

Filtreleme ekleme

Öğrenci Dizini sayfasına filtreleme eklemek için:

  • Sayfaya Razor bir metin kutusu ve gönder düğmesi eklenir. Metin kutusu, ad veya soyadı üzerinde bir arama dizesi sağlar.
  • Sayfa modeli, metin kutusu değerini kullanacak şekilde güncelleştirilir.

OnGetAsync yöntemini güncelleştirme

filtreleme eklemek için içindeki Students/Index.cshtml.cs kodu aşağıdaki kodla değiştirin:

public class IndexModel : PageModel
{
    private readonly SchoolContext _context;

    public IndexModel(SchoolContext context)
    {
        _context = context;
    }

    public string NameSort { get; set; }
    public string DateSort { get; set; }
    public string CurrentFilter { get; set; }
    public string CurrentSort { get; set; }

    public IList<Student> Students { get; set; }

    public async Task OnGetAsync(string sortOrder, string searchString)
    {
        NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        DateSort = sortOrder == "Date" ? "date_desc" : "Date";

        CurrentFilter = searchString;
        
        IQueryable<Student> studentsIQ = from s in _context.Students
                                        select s;
        if (!String.IsNullOrEmpty(searchString))
        {
            studentsIQ = studentsIQ.Where(s => s.LastName.Contains(searchString)
                                   || s.FirstMidName.Contains(searchString));
        }

        switch (sortOrder)
        {
            case "name_desc":
                studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
                break;
            case "Date":
                studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
                break;
            case "date_desc":
                studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
                break;
            default:
                studentsIQ = studentsIQ.OrderBy(s => s.LastName);
                break;
        }

        Students = await studentsIQ.AsNoTracking().ToListAsync();
    }
}

Yukarıdaki kod:

  • parametresini searchString yöntemine OnGetAsync ekler ve parametre değerini özelliğine CurrentFilter kaydeder. Arama dizesi değeri, sonraki bölümde eklenen bir metin kutusundan alınır.
  • LINQ deyimine bir Where yan tümcesi ekler. Where yan tümcesi yalnızca ad veya soyadı arama dizesini içeren öğrencileri seçer. LINQ deyimi yalnızca aranacak bir değer varsa yürütülür.

IQueryable ve IEnumerable karşılaştırması

Kod bir IQueryable nesnede Where yöntemini çağırır ve filtre sunucuda işlenir. Bazı senaryolarda uygulama, yöntemini bellek içi bir koleksiyonda uzantı yöntemi olarak çağırıyor Where olabilir. Örneğin, öğesinin bir koleksiyon döndüren IEnumerable bir depo yöntemine değiştiğini varsayalım_context.Students.EF CoreDbSet Sonuç normalde aynı olur ancak bazı durumlarda farklı olabilir.

Örneğin, .NET Framework uygulaması Contains varsayılan olarak büyük/küçük harfe duyarlı bir karşılaştırma gerçekleştirir. SQL Server'da büyük/küçük harf duyarlılığı, Contains SQL Server örneğinin harmanlama ayarı tarafından belirlenir. SQL Server varsayılan olarak büyük/küçük harfe duyarlı değildir. SQLite varsayılan olarak büyük/küçük harfe duyarlıdır. ToUpper testi açıkça büyük/küçük harfe duyarsız hale getirmek için çağrılabilir:

Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())`

Yukarıdaki kod, yöntem bir veya IEnumerable SQLite üzerinde çalıştırıldığında bile filtrenin Where büyük/küçük harfe duyarlı olmamasını sağlar.

Bir IEnumerable koleksiyonda çağrıldığında Contains .NET Core uygulaması kullanılır. Bir IQueryable nesnede çağrıldığında Contains veritabanı uygulaması kullanılır.

genellikle Contains performans nedenleriyle çağrısı IQueryable tercih edilir. ile IQueryable, filtreleme veritabanı sunucusu tarafından gerçekleştirilir. Önce bir IEnumerable oluşturulursa, tüm satırların veritabanı sunucusundan döndürülür.

çağrısı ToUpperiçin bir performans cezası vardır. Kod, ToUpper TSQL SELECT deyiminin WHERE yan tümcesine bir işlev ekler. Eklenen işlev, iyileştiricinin dizin kullanmasını engeller. SQL'in büyük/küçük harfe duyarsız olarak yüklendiği düşünüldüğünde, gerekmediğinde çağrıdan ToUpper kaçınmak en iyisidir.

Daha fazla bilgi için bkz . Sqlite sağlayıcısıyla büyük/küçük harfe duyarsız sorgu kullanma.

Sayfayı Razor güncelleştirme

Arama düğmesi eklemek için içindeki Pages/Students/Index.cshtml kodu değiştirin.

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Students";
}

<h2>Students</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form asp-page="./Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name:
            <input type="text" name="SearchString" value="@Model.CurrentFilter" />
            <input type="submit" value="Search" class="btn btn-primary" /> |
            <a asp-page="./Index">Back to full List</a>
        </p>
    </div>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
                    @Html.DisplayNameFor(model => model.Students[0].LastName)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Students[0].FirstMidName)
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
                    @Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
                </a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Students)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.EnrollmentDate)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Yukarıdaki kod, arama metin kutusunu ve düğmesini eklemek için etiket yardımcısını kullanır <form> . Varsayılan olarak, <form> etiket yardımcısı form verilerini post ile gönderir. POST ile, parametreler URL'de değil HTTP ileti gövdesinde geçirilir. HTTP GET kullanıldığında, form verileri URL'ye sorgu dizeleri olarak geçirilir. Verileri sorgu dizeleriyle geçirmek, kullanıcıların URL'ye yer işareti eklemesine olanak tanır. W3C yönergeleri, eylem güncelleştirmeyle sonuçlanmadığında GET kullanılmasını önerir.

Uygulamayı test edin:

  • Öğrenciler sekmesini seçin ve bir arama dizesi girin. SQLite kullanıyorsanız, filtre yalnızca daha önce gösterilen isteğe bağlı ToUpper kodu uyguladıysanız büyük/küçük harfe duyarlı değildir.

  • Ara'yı seçin.

URL'nin arama dizesini içerdiğine dikkat edin. Örneğin:

https://localhost:5001/Students?SearchString=an

Sayfaya yer işareti eklenirse, yer işareti sayfanın URL'sini ve sorgu dizesini SearchString içerir. method="get" etiketindekiform, sorgu dizesinin oluşturulmasına neden olandır.

Şu anda, bir sütun başlığı sıralama bağlantısı seçildiğinde, Arama kutusundaki filtre değeri kaybolur. Kayıp filtre değeri bir sonraki bölümde düzeltildi.

Sayfalama ekleme

Bu bölümde, disk belleğini desteklemek için bir PaginatedList sınıf oluşturulur. PaginatedList sınıfı, tablonun tüm satırlarını almak yerine sunucudaki verileri filtrelemek için ve Take deyimlerini kullanırSkip. Aşağıdaki çizimde sayfalama düğmeleri gösterilmektedir.

Sayfalama bağlantıları içeren öğrenciler dizin sayfası

PaginatedList sınıfını oluşturma

Proje klasöründe aşağıdaki kodla oluşturun PaginatedList.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

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

        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            this.AddRange(items);
        }

        public bool HasPreviousPage => PageIndex > 1;

        public bool HasNextPage => PageIndex < TotalPages;

        public static async Task<PaginatedList<T>> CreateAsync(
            IQueryable<T> source, int pageIndex, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip(
                (pageIndex - 1) * pageSize)
                .Take(pageSize).ToListAsync();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}

Yukarıdaki CreateAsync koddaki yöntem sayfa boyutunu ve sayfa numarasını alır ve uygun Skip ve Take deyimlerini öğesine IQueryableuygular. üzerinde IQueryableçağrıldığındaToListAsync, yalnızca istenen sayfayı içeren bir Liste döndürür. ve özellikleri HasPreviousPage HasNextPage, Önceki ve Sonraki sayfalama düğmelerini etkinleştirmek veya devre dışı bırakmak için kullanılır.

CreateAsync yöntemi, oluşturmak PaginatedList<T>için kullanılır. Oluşturucu nesneyi oluşturamaz PaginatedList<T> ; oluşturucular zaman uyumsuz kod çalıştıramaz.

Yapılandırmaya sayfa boyutu ekleme

appsettings.json Yapılandırma dosyasına ekleyinPageSize:

{
  "PageSize": 3,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=CU-1;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

IndexModel'e disk belleği ekleme

disk belleği eklemek için içindeki Students/Index.cshtml.cs kodu değiştirin.

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Students
{
    public class IndexModel : PageModel
    {
        private readonly SchoolContext _context;
        private readonly IConfiguration Configuration;

        public IndexModel(SchoolContext context, IConfiguration configuration)
        {
            _context = context;
            Configuration = configuration;
        }

        public string NameSort { get; set; }
        public string DateSort { get; set; }
        public string CurrentFilter { get; set; }
        public string CurrentSort { get; set; }

        public PaginatedList<Student> Students { get; set; }

        public async Task OnGetAsync(string sortOrder,
            string currentFilter, string searchString, int? pageIndex)
        {
            CurrentSort = sortOrder;
            NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            DateSort = sortOrder == "Date" ? "date_desc" : "Date";
            if (searchString != null)
            {
                pageIndex = 1;
            }
            else
            {
                searchString = currentFilter;
            }

            CurrentFilter = searchString;

            IQueryable<Student> studentsIQ = from s in _context.Students
                                             select s;
            if (!String.IsNullOrEmpty(searchString))
            {
                studentsIQ = studentsIQ.Where(s => s.LastName.Contains(searchString)
                                       || s.FirstMidName.Contains(searchString));
            }
            switch (sortOrder)
            {
                case "name_desc":
                    studentsIQ = studentsIQ.OrderByDescending(s => s.LastName);
                    break;
                case "Date":
                    studentsIQ = studentsIQ.OrderBy(s => s.EnrollmentDate);
                    break;
                case "date_desc":
                    studentsIQ = studentsIQ.OrderByDescending(s => s.EnrollmentDate);
                    break;
                default:
                    studentsIQ = studentsIQ.OrderBy(s => s.LastName);
                    break;
            }

            var pageSize = Configuration.GetValue("PageSize", 4);
            Students = await PaginatedList<Student>.CreateAsync(
                studentsIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
        }
    }
}

Yukarıdaki kod:

  • özelliğinin Students türünü olarak IList<Student> PaginatedList<Student>değiştirir.
  • Sayfa dizinini, geçerli sortOrderve öğesini currentFilter yöntem imzasına OnGetAsync ekler.
  • Sıralama düzenini özelliğine CurrentSort kaydeder.
  • Yeni bir arama dizesi olduğunda sayfa dizinini 1 olarak sıfırlar.
  • Öğrenci varlıklarını almak için sınıfını PaginatedList kullanır.
  • Yapılandırma başarısız olursa Yapılandırma'dan 3, 4 olarak ayarlarpageSize.

Aşağıdaki durumlarda, alan tüm parametreler OnGetAsync null olur:

  • Sayfa, Öğrenciler bağlantısından çağrılır.
  • Kullanıcı bir sayfalama veya sıralama bağlantısına tıklamadı.

Sayfalama bağlantısına tıklandığında, sayfa dizini değişkeni görüntülenecek sayfa numarasını içerir.

özelliği Sayfaya CurrentSort Razor geçerli sıralama düzenini sağlar. Sayfalama sırasında sıralama düzenini korumak için geçerli sıralama düzeninin sayfalama bağlantılarına eklenmesi gerekir.

özelliği Sayfaya CurrentFilter Razor geçerli filtre dizesini sağlar. Değer CurrentFilter :

  • Disk belleği sırasında filtre ayarlarını korumak için sayfalama bağlantılarına eklenmelidir.
  • Sayfa yeniden görüntülendiğinde metin kutusuna geri yüklenmelidir.

Sayfalama sırasında arama dizesi değiştirilirse sayfa 1'e sıfırlanır. Yeni filtre farklı verilerin görüntülenmesine neden olabileceğinden sayfanın 1'e sıfırlanması gerekir. Arama değeri girildiğinde ve Gönder seçildiğinde:

  • Arama dizesi değiştirilir.
  • searchString Parametresi null değil.

yöntemi, PaginatedList.CreateAsync öğrenci sorgusunu disk belleğini destekleyen bir koleksiyon türündeki tek bir öğrenci sayfasına dönüştürür. Tek bir öğrenci sayfası Sayfaya Razor geçirilir.

Çağrısından PaginatedList.CreateAsync sonraki pageIndex iki soru işareti null birleşim işlecini temsil eder. Null birleşim işleci, null atanabilir bir tür için varsayılan değeri tanımlar. İfade pageIndex ?? 1 , değeri pageIndex varsa değerini döndürür, aksi takdirde 1 döndürür.

Students/Index.cshtml içindeki kodu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır:

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Students";
}

<h2>Students</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form asp-page="./Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name: 
            <input type="text" name="SearchString" value="@Model.CurrentFilter" />
            <input type="submit" value="Search" class="btn btn-primary" /> |
            <a asp-page="./Index">Back to full List</a>
        </p>
    </div>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
                   asp-route-currentFilter="@Model.CurrentFilter">
                    @Html.DisplayNameFor(model => model.Students[0].LastName)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Students[0].FirstMidName)
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort"
                   asp-route-currentFilter="@Model.CurrentFilter">
                    @Html.DisplayNameFor(model => model.Students[0].EnrollmentDate)
                </a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Students)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.EnrollmentDate)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

@{
    var prevDisabled = !Model.Students.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.Students.HasNextPage ? "disabled" : "";
}

<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Students.PageIndex - 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-primary @prevDisabled">
    Previous
</a>
<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-primary @nextDisabled">
    Next
</a>

Sütun üst bilgisi bağlantıları, geçerli arama dizesini yöntemine geçirmek için sorgu dizesini OnGetAsync kullanır:

<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
   asp-route-currentFilter="@Model.CurrentFilter">
    @Html.DisplayNameFor(model => model.Students[0].LastName)
</a>

Sayfalama düğmeleri etiket yardımcıları tarafından görüntülenir:


<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Students.PageIndex - 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-primary @prevDisabled">
    Previous
</a>
<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-primary @nextDisabled">
    Next
</a>

Uygulamayı çalıştırın ve öğrenciler sayfasına gidin.

  • Sayfalamanın çalıştığından emin olmak için farklı sıralama düzenlerindeki sayfalama bağlantılarına tıklayın.
  • Sayfalamanın sıralama ve filtreleme ile doğru şekilde çalıştığını doğrulamak için bir arama dizesi girin ve sayfalandırmayı deneyin.

sayfalama bağlantıları içeren öğrenciler dizin sayfası

Gruplama

Bu bölüm, her kayıt tarihi için kaç öğrencinin kaydolduğunu gösteren bir About sayfa oluşturur. Güncelleştirme gruplandırma kullanır ve aşağıdaki adımları içerir:

  • Sayfa tarafından About kullanılan veriler için bir görünüm modeli oluşturun.
  • About Görünüm modelini kullanmak için sayfayı güncelleştirin.

Görünüm modelini oluşturma

Models/SchoolViewModels klasörü oluşturun.

Aşağıdaki kodla oluşturun SchoolViewModels/EnrollmentDateGroup.cs :

using System;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class EnrollmentDateGroup
    {
        [DataType(DataType.Date)]
        public DateTime? EnrollmentDate { get; set; }

        public int StudentCount { get; set; }
    }
}

Sayfayı Razor Oluşturma

Aşağıdaki kodla bir Pages/About.cshtml dosya oluşturun:

@page
@model ContosoUniversity.Pages.AboutModel

@{
    ViewData["Title"] = "Student Body Statistics";
}

<h2>Student Body Statistics</h2>

<table>
    <tr>
        <th>
            Enrollment Date
        </th>
        <th>
            Students
        </th>
    </tr>

    @foreach (var item in Model.Students)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @item.StudentCount
            </td>
        </tr>
    }
</table>

Sayfa modelini oluşturma

Pages/About.cshtml.cs Dosyayı aşağıdaki kodla güncelleştirin:

using ContosoUniversity.Models.SchoolViewModels;
using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContosoUniversity.Models;

namespace ContosoUniversity.Pages
{
    public class AboutModel : PageModel
    {
        private readonly SchoolContext _context;

        public AboutModel(SchoolContext context)
        {
            _context = context;
        }

        public IList<EnrollmentDateGroup> Students { get; set; }

        public async Task OnGetAsync()
        {
            IQueryable<EnrollmentDateGroup> data =
                from student in _context.Students
                group student by student.EnrollmentDate into dateGroup
                select new EnrollmentDateGroup()
                {
                    EnrollmentDate = dateGroup.Key,
                    StudentCount = dateGroup.Count()
                };

            Students = await data.AsNoTracking().ToListAsync();
        }
    }
}

LINQ deyimi, öğrenci varlıklarını kayıt tarihine göre gruplandırır, her gruptaki varlık sayısını hesaplar ve sonuçları bir görünüm modeli nesneleri koleksiyonunda EnrollmentDateGroup depolar.

Uygulamayı çalıştırın ve Hakkında sayfasına gidin. Her kayıt tarihi için öğrenci sayısı bir tabloda görüntülenir.

Sayfa hakkında

Sonraki adımlar

Sonraki öğreticide uygulama, veri modelini güncelleştirmek için geçişleri kullanır.

Bu öğreticide sıralama, filtreleme, gruplandırma ve sayfalama işlevleri eklenir.

Aşağıdaki çizimde tamamlanmış bir sayfa gösterilmektedir. Sütun başlıkları, sütunu sıralamak için tıklanabilir bağlantılardır. Bir sütun başlığına art arda tıklandığında artan ve azalan sıralama düzeni arasında geçiş yapar.

Öğrenciler dizin sayfası

Çözemediğiniz sorunlarla karşılaşırsanız tamamlanmış uygulamayı indirin.

Dizin sayfasına sıralama ekleme

sıralama parametrelerini içerecek şekilde öğesine dizeler Students/Index.cshtml.cs PageModel ekleyin:

public class IndexModel : PageModel
{
    private readonly SchoolContext _context;

    public IndexModel(SchoolContext context)
    {
        _context = context;
    }

    public string NameSort { get; set; }
    public string DateSort { get; set; }
    public string CurrentFilter { get; set; }
    public string CurrentSort { get; set; }

öğesini Students/Index.cshtml.cs OnGetAsync aşağıdaki kodla güncelleştirin:

public async Task OnGetAsync(string sortOrder)
{
    NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    DateSort = sortOrder == "Date" ? "date_desc" : "Date";

    IQueryable<Student> studentIQ = from s in _context.Student
                                    select s;

    switch (sortOrder)
    {
        case "name_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            studentIQ = studentIQ.OrderBy(s => s.LastName);
            break;
    }

    Student = await studentIQ.AsNoTracking().ToListAsync();
}

Yukarıdaki kod, URL'deki sorgu dizesinden bir sortOrder parametre alır. URL (sorgu dizesi dahil) Bağlayıcı Etiketi Yardımcısı tarafından oluşturulur

sortOrder Parametre "Ad" veya "Tarih" şeklindedir. Parametresi sortOrder isteğe bağlı olarak azalan sırayı belirtmek için "_desc" tarafından takip edilir. Varsayılan sıralama düzeni artan düzendedir.

Öğrenciler bağlantısından Dizin sayfası istendiğinde sorgu dizesi yoktur. Öğrenciler soyadına göre artan düzende görüntülenir. Soyadına göre artan düzen, deyimdeki switch varsayılandır (küçük harf). Kullanıcı bir sütun başlığı bağlantısına tıkladığında, sorgu dizesi değerinde uygun sortOrder değer sağlanır.

NameSort ve DateSort Sayfa tarafından Razor sütun başlığı köprülerini uygun sorgu dizesi değerleriyle yapılandırmak için kullanılır:

public async Task OnGetAsync(string sortOrder)
{
    NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    DateSort = sortOrder == "Date" ? "date_desc" : "Date";

    IQueryable<Student> studentIQ = from s in _context.Student
                                    select s;

    switch (sortOrder)
    {
        case "name_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            studentIQ = studentIQ.OrderBy(s => s.LastName);
            break;
    }

    Student = await studentIQ.AsNoTracking().ToListAsync();
}

Aşağıdaki kod C# koşullu ?: işlecini içerir:

NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
DateSort = sortOrder == "Date" ? "date_desc" : "Date";

İlk satır null veya boş NameSort olduğunda sortOrder "name_desc" olarak ayarlandığını belirtir. Null veya boş değilsesortOrder, NameSort boş bir dizeye ayarlanır.

, ?: operator üçüncül işleç olarak da bilinir.

Bu iki deyim, sayfanın sütun başlığı köprülerini aşağıdaki gibi ayarlamasını sağlar:

Geçerli sıralama düzeni Soyadı Köprüsü Tarih Köprüsü
Soyadı artan azalan düzende ascending
Soyadı azalan ascending ascending
Artan tarih ascending azalan düzende
Azalan tarih ascending ascending

yöntemi, sıralama ölçütü olarak sütunu belirtmek için LINQ to Entities kullanır. Kod switch deyiminden önce bir IQueryable<Student> başlatır ve switch deyiminde değiştirir:

public async Task OnGetAsync(string sortOrder)
{
    NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    DateSort = sortOrder == "Date" ? "date_desc" : "Date";

    IQueryable<Student> studentIQ = from s in _context.Student
                                    select s;

    switch (sortOrder)
    {
        case "name_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            studentIQ = studentIQ.OrderBy(s => s.LastName);
            break;
    }

    Student = await studentIQ.AsNoTracking().ToListAsync();
}

birIQueryable oluşturulduğunda veya değiştirildiğinde veritabanına hiçbir sorgu gönderilmez. Nesne bir koleksiyona dönüştürülene kadar IQueryable sorgu yürütülemez. IQueryable gibi ToListAsyncbir yöntemi çağırarak bir koleksiyona dönüştürülür. Bu nedenle kod, aşağıdaki deyime IQueryable kadar yürütülmeyen tek bir sorguyla sonuçlanmıştır:

Student = await studentIQ.AsNoTracking().ToListAsync();

OnGetAsync çok sayıda sıralanabilir sütunla ayrıntılı olabilir.

içindeki Students/Index.cshtmlkodu aşağıdaki vurgulanmış kodla değiştirin:

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>
<p>
    <a asp-page="Create">Create New</a>
</p>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
                    @Html.DisplayNameFor(model => model.Student[0].LastName)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Student[0].FirstMidName)
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
                    @Html.DisplayNameFor(model => model.Student[0].EnrollmentDate)
                </a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Student)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.EnrollmentDate)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Yukarıdaki kod:

  • ve EnrollmentDate sütun başlıklarına LastName köprü ekler.
  • geçerli sıralama düzeni değerleriyle köprüler ayarlamak için ve DateSort içindeki bilgileri NameSort kullanır.

Sıralamanın çalıştığını doğrulamak için:

  • Uygulamayı çalıştırın ve Öğrenciler sekmesini seçin.
  • Soyadı'ya tıklayın.
  • Kayıt Tarihi'ne tıklayın.

Kodu daha iyi anlamak için:

  • içinde Students/Index.cshtml.cs, üzerinde switch (sortOrder)bir kesme noktası ayarlayın.
  • ve DateSortiçin NameSort bir saat ekleyin.
  • içinde Students/Index.cshtml, üzerinde @Html.DisplayNameFor(model => model.Student[0].LastName)bir kesme noktası ayarlayın.

Hata ayıklayıcıda adım adım ilerleyin.

Öğrenci Dizini sayfasına Arama Kutusu Ekleme

Öğrenci Dizini sayfasına filtreleme eklemek için:

  • Sayfaya Razor bir metin kutusu ve gönder düğmesi eklenir. Metin kutusu, ad veya soyadı üzerinde bir arama dizesi sağlar.
  • Sayfa modeli, metin kutusu değerini kullanacak şekilde güncelleştirilir.

Index yöntemine filtreleme işlevi ekleme

öğesini Students/Index.cshtml.cs OnGetAsync aşağıdaki kodla güncelleştirin:

public async Task OnGetAsync(string sortOrder, string searchString)
{
    NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    DateSort = sortOrder == "Date" ? "date_desc" : "Date";
    CurrentFilter = searchString;

    IQueryable<Student> studentIQ = from s in _context.Student
                                    select s;
    if (!String.IsNullOrEmpty(searchString))
    {
        studentIQ = studentIQ.Where(s => s.LastName.Contains(searchString)
                               || s.FirstMidName.Contains(searchString));
    }

    switch (sortOrder)
    {
        case "name_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            studentIQ = studentIQ.OrderBy(s => s.LastName);
            break;
    }

    Student = await studentIQ.AsNoTracking().ToListAsync();
}

Yukarıdaki kod:

  • parametresini searchString yöntemine OnGetAsync ekler. Arama dizesi değeri, sonraki bölümde eklenen bir metin kutusundan alınır.
  • LINQ deyimine bir Where yan tümcesi eklendi. Where yan tümcesi yalnızca ad veya soyadı arama dizesini içeren öğrencileri seçer. LINQ deyimi yalnızca aranacak bir değer varsa yürütülür.

Not: Yukarıdaki kod bir IQueryable nesnede Where yöntemini çağırır ve filtre sunucuda işlenir. Bazı senaryolarda uygulama, yöntemini bellek içi bir koleksiyonda uzantı yöntemi olarak çağırıyor Where olabilir. Örneğin, öğesinin bir koleksiyon döndüren IEnumerable bir depo yöntemine değiştiğini varsayalım_context.Students.EF CoreDbSet Sonuç normalde aynı olur ancak bazı durumlarda farklı olabilir.

Örneğin, .NET Framework uygulaması Contains varsayılan olarak büyük/küçük harfe duyarlı bir karşılaştırma gerçekleştirir. SQL Server'da büyük/küçük harf duyarlılığı, Contains SQL Server örneğinin harmanlama ayarı tarafından belirlenir. SQL Server varsayılan olarak büyük/küçük harfe duyarlı değildir. ToUpper testi açıkça büyük/küçük harfe duyarsız hale getirmek için çağrılabilir:

Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())

Yukarıdaki kod, kod kullanılacak IEnumerableşekilde değiştirilirse sonuçların büyük/küçük harfe duyarlı olmamasını sağlar. Bir IEnumerable koleksiyonda çağrıldığında Contains .NET Core uygulaması kullanılır. Bir IQueryable nesnede çağrıldığında Contains veritabanı uygulaması kullanılır. IEnumerable Bir depodan döndürmenin önemli bir performans cezası olabilir:

  1. Tüm satırlar DB sunucusundan döndürülür.
  2. Filtre, uygulamadaki döndürülen tüm satırlara uygulanır.

çağrısı ToUpperiçin bir performans cezası vardır. Kod, ToUpper TSQL SELECT deyiminin WHERE yan tümcesine bir işlev ekler. Eklenen işlev, iyileştiricinin dizin kullanmasını engeller. SQL'in büyük/küçük harfe duyarsız olarak yüklendiği düşünüldüğünde, gerekmediğinde çağrıdan ToUpper kaçınmak en iyisidir.

Öğrenci Dizini sayfasına Arama Kutusu ekleme

içindePages/Students/Index.cshtml, bir Arama düğmesi ve çeşitli chrome oluşturmak için aşağıdaki vurgulanmış kodu ekleyin.

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form asp-page="./Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name:
            <input type="text" name="SearchString" value="@Model.CurrentFilter" />
            <input type="submit" value="Search" class="btn btn-default" /> |
            <a asp-page="./Index">Back to full List</a>
        </p>
    </div>
</form>

<table class="table">

Yukarıdaki kod, arama metin kutusunu ve düğmesini eklemek için etiket yardımcısını kullanır <form> . Varsayılan olarak, <form> etiket yardımcısı form verilerini post ile gönderir. POST ile, parametreler URL'de değil HTTP ileti gövdesinde geçirilir. HTTP GET kullanıldığında, form verileri URL'ye sorgu dizeleri olarak geçirilir. Verileri sorgu dizeleriyle geçirmek, kullanıcıların URL'ye yer işareti eklemesine olanak tanır. W3C yönergeleri, eylem güncelleştirmeyle sonuçlanmadığında GET kullanılmasını önerir.

Uygulamayı test edin:

  • Öğrenciler sekmesini seçin ve bir arama dizesi girin.
  • Ara'yı seçin.

URL'nin arama dizesini içerdiğine dikkat edin.

http://localhost:5000/Students?SearchString=an

Sayfaya yer işareti eklenirse, yer işareti sayfanın URL'sini ve sorgu dizesini SearchString içerir. method="get" etiketindekiform, sorgu dizesinin oluşturulmasına neden olandır.

Şu anda, bir sütun başlığı sıralama bağlantısı seçildiğinde, Arama kutusundaki filtre değeri kaybolur. Kayıp filtre değeri bir sonraki bölümde düzeltildi.

Öğrenci Dizini sayfasına disk belleği işlevi ekleme

Bu bölümde, disk belleğini desteklemek için bir PaginatedList sınıf oluşturulur. PaginatedList sınıfı, tablonun tüm satırlarını almak yerine sunucudaki verileri filtrelemek için ve Take deyimlerini kullanırSkip. Aşağıdaki çizimde sayfalama düğmeleri gösterilmektedir.

Sayfalama bağlantıları içeren öğrenciler dizin sayfası

Proje klasöründe aşağıdaki kodla oluşturun PaginatedList.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

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

        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            this.AddRange(items);
        }

        public bool HasPreviousPage => PageIndex > 1;

        public bool HasNextPage => PageIndex < TotalPages;

        public static async Task<PaginatedList<T>> CreateAsync(
            IQueryable<T> source, int pageIndex, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip(
                (pageIndex - 1) * pageSize)
                .Take(pageSize).ToListAsync();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}

Yukarıdaki CreateAsync koddaki yöntem sayfa boyutunu ve sayfa numarasını alır ve uygun Skip ve Take deyimlerini öğesine IQueryableuygular. üzerinde IQueryableçağrıldığındaToListAsync, yalnızca istenen sayfayı içeren bir Liste döndürür. ve özellikleri HasPreviousPage HasNextPage, Önceki ve Sonraki sayfalama düğmelerini etkinleştirmek veya devre dışı bırakmak için kullanılır.

CreateAsync yöntemi, oluşturmak PaginatedList<T>için kullanılır. Oluşturucu nesneyi oluşturamaz PaginatedList<T> , oluşturucular zaman uyumsuz kod çalıştıramaz.

Dizin yöntemine disk belleği işlevi ekleme

içinde Students/Index.cshtml.cs, öğesinin Student IList<Student> türünü olarak PaginatedList<Student>güncelleştirin:

public PaginatedList<Student> Student { get; set; }

öğesini Students/Index.cshtml.cs OnGetAsync aşağıdaki kodla güncelleştirin:

public async Task OnGetAsync(string sortOrder,
    string currentFilter, string searchString, int? pageIndex)
{
    CurrentSort = sortOrder;
    NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    DateSort = sortOrder == "Date" ? "date_desc" : "Date";
    if (searchString != null)
    {
        pageIndex = 1;
    }
    else
    {
        searchString = currentFilter;
    }

    CurrentFilter = searchString;

    IQueryable<Student> studentIQ = from s in _context.Student
                                    select s;
    if (!String.IsNullOrEmpty(searchString))
    {
        studentIQ = studentIQ.Where(s => s.LastName.Contains(searchString)
                               || s.FirstMidName.Contains(searchString));
    }
    switch (sortOrder)
    {
        case "name_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            studentIQ = studentIQ.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            studentIQ = studentIQ.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            studentIQ = studentIQ.OrderBy(s => s.LastName);
            break;
    }

    int pageSize = 3;
    Student = await PaginatedList<Student>.CreateAsync(
        studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);
}

Yukarıdaki kod, sayfa dizinini, geçerli sortOrderve öğesini currentFilter yöntem imzasına ekler.

public async Task OnGetAsync(string sortOrder,
    string currentFilter, string searchString, int? pageIndex)

Aşağıdaki durumlarda tüm parametreler null olur:

  • Sayfa, Öğrenciler bağlantısından çağrılır.
  • Kullanıcı bir sayfalama veya sıralama bağlantısına tıklamadı.

Sayfalama bağlantısına tıklandığında, sayfa dizini değişkeni görüntülenecek sayfa numarasını içerir.

CurrentSortRazor Geçerli sıralama düzenini içeren Sayfayı sağlar. Sayfalama sırasında sıralama düzenini korumak için geçerli sıralama düzeninin sayfalama bağlantılarına eklenmesi gerekir.

CurrentFilter Geçerli filtre dizesini Razor içeren Sayfayı sağlar. Değer CurrentFilter :

  • Disk belleği sırasında filtre ayarlarını korumak için sayfalama bağlantılarına eklenmelidir.
  • Sayfa yeniden görüntülendiğinde metin kutusuna geri yüklenmelidir.

Sayfalama sırasında arama dizesi değiştirilirse sayfa 1'e sıfırlanır. Yeni filtre farklı verilerin görüntülenmesine neden olabileceğinden sayfanın 1'e sıfırlanması gerekir. Arama değeri girildiğinde ve Gönder seçildiğinde:

  • Arama dizesi değiştirilir.
  • searchString Parametresi null değil.
if (searchString != null)
{
    pageIndex = 1;
}
else
{
    searchString = currentFilter;
}

yöntemi, PaginatedList.CreateAsync öğrenci sorgusunu disk belleğini destekleyen bir koleksiyon türündeki tek bir öğrenci sayfasına dönüştürür. Tek bir öğrenci sayfası Sayfaya Razor geçirilir.

Student = await PaginatedList<Student>.CreateAsync(
    studentIQ.AsNoTracking(), pageIndex ?? 1, pageSize);

içindeki PaginatedList.CreateAsync iki soru işareti null birleşim işlecini temsil eder. Null birleşim işleci, null atanabilir bir tür için varsayılan değeri tanımlar. İfade (pageIndex ?? 1) , değeri pageIndex varsa değerini döndürme anlamına gelir. Değer yoksa pageIndex 1 değerini döndürür.

içindeki Students/Index.cshtmlişaretlemeyi güncelleştirin. Değişiklikler vurgulanır:

@page
@model ContosoUniversity.Pages.Students.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form asp-page="./Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name: <input type="text" name="SearchString" value="@Model.CurrentFilter" />
            <input type="submit" value="Search" class="btn btn-default" /> |
            <a asp-page="./Index">Back to full List</a>
        </p>
    </div>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
                   asp-route-currentFilter="@Model.CurrentFilter">
                    @Html.DisplayNameFor(model => model.Student[0].LastName)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Student[0].FirstMidName)
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort"
                   asp-route-currentFilter="@Model.CurrentFilter">
                    @Html.DisplayNameFor(model => model.Student[0].EnrollmentDate)
                </a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Student)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.EnrollmentDate)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

@{
    var prevDisabled = !Model.Student.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.Student.HasNextPage ? "disabled" : "";
}

<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Student.PageIndex - 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-default @prevDisabled">
    Previous
</a>
<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Student.PageIndex + 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-default @nextDisabled">
    Next
</a>

Sütun üst bilgisi bağlantıları, kullanıcının filtre sonuçları içinde sıralama gerçekleştirebilmesi için geçerli arama dizesini yöntemine geçirmek için OnGetAsync sorgu dizesini kullanır:

<a asp-page="./Index" asp-route-sortOrder="@Model.NameSort"
   asp-route-currentFilter="@Model.CurrentFilter">
    @Html.DisplayNameFor(model => model.Student[0].LastName)
</a>

Sayfalama düğmeleri etiket yardımcıları tarafından görüntülenir:


<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Student.PageIndex - 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-default @prevDisabled">
    Previous
</a>
<a asp-page="./Index"
   asp-route-sortOrder="@Model.CurrentSort"
   asp-route-pageIndex="@(Model.Student.PageIndex + 1)"
   asp-route-currentFilter="@Model.CurrentFilter"
   class="btn btn-default @nextDisabled">
    Next
</a>

Uygulamayı çalıştırın ve öğrenciler sayfasına gidin.

  • Sayfalamanın çalıştığından emin olmak için farklı sıralama düzenlerindeki sayfalama bağlantılarına tıklayın.
  • Sayfalamanın sıralama ve filtreleme ile doğru şekilde çalıştığını doğrulamak için bir arama dizesi girin ve sayfalandırmayı deneyin.

sayfalama bağlantıları içeren öğrenciler dizin sayfası

Kodu daha iyi anlamak için:

  • içinde Students/Index.cshtml.cs, üzerinde switch (sortOrder)bir kesme noktası ayarlayın.
  • , , DateSortCurrentSortve Model.Student.PageIndexiçin NameSortbir saat ekleyin.
  • içinde Students/Index.cshtml, üzerinde @Html.DisplayNameFor(model => model.Student[0].LastName)bir kesme noktası ayarlayın.

Hata ayıklayıcıda adım adım ilerleyin.

Öğrenci istatistiklerini göstermek için Hakkında sayfasını güncelleştirme

Bu adımda, Pages/About.cshtml her kayıt tarihi için kaç öğrencinin kaydolduysa görüntülenecek şekilde güncelleştirilir. Güncelleştirme gruplandırma kullanır ve aşağıdaki adımları içerir:

  • Hakkında Sayfası tarafından kullanılan veriler için bir görünüm modeli oluşturun.
  • Görünüm modelini kullanmak için Hakkında sayfasını güncelleştirin.

Görünüm modelini oluşturma

Modeller klasöründe bir SchoolViewModels klasörü oluşturun.

SchoolViewModels klasörüne aşağıdaki kodla bir EnrollmentDateGroup.cs ekleyin:

using System;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class EnrollmentDateGroup
    {
        [DataType(DataType.Date)]
        public DateTime? EnrollmentDate { get; set; }

        public int StudentCount { get; set; }
    }
}

Hakkında sayfa modelini güncelleştirme

ASP.NET Core 2.2'deki web şablonları Hakkında sayfasını içermez. ASP.NET Core 2.2 kullanıyorsanız Hakkında Razor Sayfasını oluşturun.

Pages/About.cshtml.cs Dosyayı aşağıdaki kodla güncelleştirin:

using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContosoUniversity.Models;

namespace ContosoUniversity.Pages
{
    public class AboutModel : PageModel
    {
        private readonly SchoolContext _context;

        public AboutModel(SchoolContext context)
        {
            _context = context;
        }

        public IList<EnrollmentDateGroup> Student { get; set; }

        public async Task OnGetAsync()
        {
            IQueryable<EnrollmentDateGroup> data =
                from student in _context.Student
                group student by student.EnrollmentDate into dateGroup
                select new EnrollmentDateGroup()
                {
                    EnrollmentDate = dateGroup.Key,
                    StudentCount = dateGroup.Count()
                };

            Student = await data.AsNoTracking().ToListAsync();
        }
    }
}

LINQ deyimi, öğrenci varlıklarını kayıt tarihine göre gruplandırır, her gruptaki varlık sayısını hesaplar ve sonuçları bir görünüm modeli nesneleri koleksiyonunda EnrollmentDateGroup depolar.

Hakkında Razor Sayfasını Değiştirme

Dosyadaki Pages/About.cshtml kodu aşağıdaki kodla değiştirin:

@page
@model ContosoUniversity.Pages.AboutModel

@{
    ViewData["Title"] = "Student Body Statistics";
}

<h2>Student Body Statistics</h2>

<table>
    <tr>
        <th>
            Enrollment Date
        </th>
        <th>
            Students
        </th>
    </tr>

    @foreach (var item in Model.Student)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @item.StudentCount
            </td>
        </tr>
    }
</table>

Uygulamayı çalıştırın ve Hakkında sayfasına gidin. Her kayıt tarihi için öğrenci sayısı bir tabloda görüntülenir.

Çözemediğiniz sorunlarla karşılaşırsanız, bu aşama için tamamlanmış uygulamayı indirin.

Sayfa hakkında

Ek kaynaklar

Sonraki öğreticide uygulama, veri modelini güncelleştirmek için geçişleri kullanır.