bölüm 3, Razor ASP.NET Core sıralama, filtreleme, sayfalama EF Core olan sayfalar

Tom Dykstra, Jeremy Liklikve Jon P Smith tarafından

Contoso Üniversitesi web uygulaması, Razor EF Core ve Visual Studio kullanarak nasıl sayfa Web uygulamaları oluşturacağınızı gösterir. Öğretici serisi hakkında daha fazla bilgi için ilk öğreticiyebakın.

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

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

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

Öğrenciler Dizin sayfası

Sıralama Ekle

Pages/öğrenciler/Index. cshtml. cs içindeki kodu, sıralama eklemek için 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:

  • Ekleme gerektirir using System; .
  • Sıralama parametrelerini içeren özellikleri ekler.
  • StudentÖzelliğin adını olarak değiştirir Students .
  • Yöntemindeki kodu değiştirir OnGetAsync .

OnGetAsyncYöntemi, sortOrder URL 'deki sorgu dizesinden bir parametre alır. URL ve sorgu dizesi, tutturucu etiketi Yardımcısıtarafından oluşturulur.

sortOrderParametresi ya da olur Name Date . sortOrderParametresi, isteğe bağlı olarak _desc azalan sıra belirtmek için tarafından izlenir. Varsayılan sıralama düzeni artan.

Öğrenciler bağlantısından Dizin sayfası istendiğinde sorgu dizesi yoktur. Öğrenciler, son ada göre artan sırada görüntülenir. Son ada göre artan sıralama, default switch deyimdir. Kullanıcı bir sütun başlığı bağlantısına tıkladığında, sortOrder sorgu dizesi değerinde uygun değer sağlanır.

NameSort ve DateSort 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?:. ?:İşleci üçlü bir işleçtir, üç işlenen alır. İlk satır, sortOrder null veya boş olduğunu belirtir, NameSort olarak ayarlanır name_desc . sortOrderNull veya boş değilse NameSort boş bir dize olarak ayarlanır.

Bu iki deyim, sayfanın sütun başlığı köprülerini şu şekilde ayarlamanızı sağlar:

Geçerli sıralama düzeni Son ad Köprüsü Tarih Köprüsü
Artan son ad descending ascending
Azalan son ad ascending ascending
Artan Tarih ascending descending
Azalan Tarih ascending ascending

yöntemi, sıralama yapılacak sütunu belirtmek için LINQ to Entities kullanır. Kod, IQueryable<Student> Switch ifadesinden önce bir başlatır ve Switch ifadesinde onu 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. IQueryableNesne bir koleksiyona dönüştürülene kadar sorgu yürütülmez. IQueryable , gibi bir yöntemi çağırarak bir koleksiyona dönüştürülür ToListAsync . Bu nedenle, IQueryable kod, aşağıdaki deyime kadar yürütülemeyen tek bir sorgu ile sonuçlanır:

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

OnGetAsync çok sayıda sıralanabilir sütunla ayrıntı alabilir. Bu işlevi kodun alternatif bir yolu hakkında daha fazla bilgi için, bu öğretici serisinin MVC sürümünde kodu basitleştirmek için dınamık LINQ kullanma konusuna bakın.

Öğrenciler/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>

<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:

  • LastNameVe sütun başlıklarına köprüler ekler EnrollmentDate .
  • , Ve içindeki bilgileri NameSort kullanarak DateSort geçerli sıralama düzeni değerleriyle köprüler ayarlar.
  • Sayfa başlığını dizinden öğrencilerle değiştirir.
  • Üzerinde yapılan değişiklikler Model.Student Model.Students .

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.

Filtre ekleme

Öğrenciler dizin sayfasına filtre eklemek için:

  • Sayfaya bir metin kutusu ve bir Gönder düğmesi eklenir Razor . 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

Öğrenciler/Index. cshtml. cs dosyasındaki kodu, filtreleme eklemek için 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:

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

IQueryable vs. IEnumerable

Kod, Where yöntemi bir nesne üzerinde çağırır IQueryable ve filtre sunucuda işlenir. Bazı senaryolarda, uygulama Where bir bellek içi koleksiyonda bir genişletme yöntemi olarak yöntemi çağırıyor olabilir. Örneğin, _context.Students EF Core 'den DbSet bir koleksiyonu döndüren bir depo yöntemine yapılan değişiklikleri varsayın IEnumerable . Sonuç normalde aynı olur, ancak bazı durumlarda farklı olabilir.

örneğin, uygulamasının .NET Framework uygulanması Contains varsayılan olarak büyük/küçük harfe duyarlı bir karşılaştırma gerçekleştirir. SQL Server, Contains büyük/küçük harf duyarlılığı SQL Server örneğinin harmanlama ayarına göre belirlenir. SQL Server varsayılan olarak büyük/küçük harfe duyarlı değildir. SQLite, büyük/küçük harfe duyarlı olur. ToUpper testi açık 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, Where yöntemin bir veya bir SQLite üzerinde çağrılması durumunda bile filtrenin büyük/küçük harf duyarsız olmasını güvence altına IEnumerable alır.

ContainsBir koleksiyon üzerinde çağrıldığında IEnumerable .NET Core uygulamasını kullanır. ContainsBir nesne üzerinde çağrıldığında IQueryable , veritabanı uygulamasını kullanır.

ContainsBir üzerinde çağırmak IQueryable , genellikle performans nedenleriyle tercih edilir. İle IQueryable , filtreleme veritabanı sunucusu tarafından yapılır. Önce bir IEnumerable oluşturulduysa, tüm satırların veritabanı sunucusundan döndürülmesi gerekir.

Çağırmak için bir performans cezası vardır ToUpper . ToUpperKod, TSQL Select IFADESININ WHERE yan tümcesine bir işlev ekler. Eklenen işlev, iyileştiricinin bir dizin kullanmasını önler. SQL, büyük/küçük harfe duyarsız olarak yüklendiği için, gerekli olmadığında çağrının önüne geçmek en iyisidir ToUpper .

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

Sayfayı Güncelleştir Razor

Pages/Students/Index.cshtml'de kodu değiştirarak bir Ara düğmesi ekleyin.

@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 kodda, <form> arama metin kutusunu ve düğmesini eklemek için etiket yardımcısı ılmaktadır. Varsayılan olarak etiket <form> yardımcısı form verilerini post ile birlikte göndermektedir. POST ile parametreler URL'de değil HTTP ileti gövdesinde geçirildi. HTTP GET kullanılırken, form verileri URL'ye sorgu dizeleri olarak geçiri. Verileri sorgu dizeleriyle geçirme, kullanıcıların URL'ye yer işareti eklemesini sağlar. W3C yönergeleri, eylem bir güncelleştirmeyle sonuçlanmayacaksa GET'in kullanılması önerilir.

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ı kodu kullandıysanız ToUpper büyük/büyük/büyük harfe duyarlı değildir.

  • Ara'ya seçin.

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

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

Sayfa yer işaretine eklenirse, yer işareti sayfanın URL'sini ve sorgu SearchString dizesini içerir. method="get"Etiketteki form , sorgu dizesinin oluşturularak oluşturulan değerdir.

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

Sayfalama ekleme

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

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

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

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

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 kodda yer alan yöntemi sayfa boyutunu ve sayfa numarasını alır ve uygun ve Skip Take deyimlerini 'ye IQueryable uygular. üzerinde ToListAsync çağrıldı IQueryable mı, yalnızca istenen sayfayı içeren bir Liste döndürür. ve HasPreviousPage özellikleri, HasNextPage Önceki ve Sonraki disk belleği düğmelerini etkinleştirmek veya devre dışı bırakmak için kullanılır.

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

Yapılandırmaya sayfa boyutu ekleme

Yapılandırma PageSize dosyasına appsettings.json ekleyin:

{
  "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 sayfalama ekleme

Sayfalama eklemek için Students/Index.cshtml.cs'de 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 türünü Students olarak IList<Student> PaginatedList<Student> değiştirir.
  • Sayfa dizinini, geçerli sortOrder ve 'i currentFilter yöntem OnGetAsync imzasını ekler.
  • Sıralama sıralamayı özelliğine CurrentSort kaydeder.
  • Yeni bir arama dizesi olduğunda sayfa dizinini 1'e sıfırlar.
  • Öğrenci PaginatedList varlıklarını almak için sınıfını kullanır.
  • Yapılandırma pageSize başarısız olursa Yapılandırma, 4'te 3 olarak ayarlar.

Aşağıdakiler olduğunda, alan OnGetAsync 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ıklanan sayfa dizini değişkeni, görüntü verilecek sayfa numarasını içerir.

özelliği, CurrentSort Razor Sayfayı geçerli sıralama düzeniyle birlikte sağlar. Sayfalama sırasında sıralama sıralama düzeni tutmak için geçerli sıralama düzeni sayfalama bağlantılarına dahil edilecektir.

özelliği, CurrentFilter Razor Sayfa'ya geçerli filtre dizesini sağlar. CurrentFilterDeğer:

  • Sayfalama sırasında filtre ayarlarını korumak için sayfalama bağlantılarına dahil edilecektir.
  • Sayfa yeniden oynatıldı olduğunda metin kutusuna geri yüklenecektir.

Sayfalama sırasında arama dizesi değiştirilirse sayfa 1'e sıfırlanır. Yeni filtre farklı verilerin görüntülenmeye neden olması nedeniyle sayfanın 1'e sıfır olması gerekir. Bir arama değeri girilir ve Gönder seçildiğinde:

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

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

çağrısında sonrasındaki pageIndex iki soru PaginatedList.CreateAsync işareti, null bir araya çağırma işlecini temsil ediyor. Null-birleştirilebilir bir tür için varsayılan bir değer tanımlar. ifadesi pageIndex ?? 1 değerine pageIndex sahipse değerini döndürür, aksi takdirde 1 değerini döndürür.

Students/Index.cshtml 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çmek 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ışır olduğundan emin olmak için farklı sıralama sıralarında sayfalama bağlantılarına tıklayın.
  • Disk belleğinin sıralama ve filtreleme ile düzgün çalıştığını doğrulamak için bir arama dizesi girin ve disk belleğini deneyin.

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

Gruplandırma

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

  • Sayfa tarafından kullanılan veriler için bir görünüm modeli About oluşturun.
  • Görünüm About 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 SchoolViewModels/EnrollmentDateGroup.cs oluşturun:

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

Sayfa Razor oluşturma

Aşağıdaki kodla bir Pages/About.cshtml dosyası 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 dosyasını 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 gruplar, her gruptaki varlık sayısını hesaplar ve sonuçları bir görünüm EnrollmentDateGroup modeli nesneleri koleksiyonunda 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, gruplama ve disk belleği işlevleri eklenmiştir.

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

Öğrenciler dizin sayfası

Çöze olmadığınız bir sorunla karşılanırsanız, tamamlanmış uygulamayı indirin.

Dizin sayfasına sıralama ekleme

Sıralama parametrelerini içermesi için Students/Index.cshtml.cs'ye PageModel dize 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; }

Students/Index.cshtml.cs dosyasını 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, sortOrder URL'de sorgu dizesinde bir parametre alır. URL (sorgu dizesi dahil) Sabit Noktası Etiketi Yardımcısı tarafından oluşturulur

parametresi sortOrder "Name" veya "Date" şeklindedir. sortOrderParametre, isteğe bağlı olarak azalan sıra belirtmek için "_DESC" tarafından izlenir. Varsayılan sıralama düzeni artan.

Öğrenciler bağlantısından Dizin sayfası istendiğinde sorgu dizesi yoktur. Öğrenciler, son ada göre artan sırada görüntülenir. Son ada göre artan sıralama, deyimindeki varsayılan (gelen durumdur) switch . Kullanıcı bir sütun başlığı bağlantısına tıkladığında, sortOrder sorgu dizesi değerinde uygun değer sağlanır.

NameSort ve DateSort 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şleciniiçerir:

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

İlk satır, sortOrder null veya boş olduğunu belirtir, NameSort "name_desc" olarak ayarlanır. sortOrderNull veya boş değilse NameSort boş bir dize olarak ayarlanır.

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

Bu iki deyim, sayfanın sütun başlığı köprülerini şu şekilde ayarlamanızı sağlar:

Geçerli sıralama düzeni Son ad Köprüsü Tarih Köprüsü
Artan son ad descending ascending
Azalan son ad ascending ascending
Artan Tarih ascending descending
Azalan Tarih ascending ascending

yöntemi, sıralama yapılacak sütunu belirtmek için LINQ to Entities kullanır. Kod, IQueryable<Student> Switch ifadesinden önce bir başlatır ve Switch ifadesinde onu 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();
}

Bir IQueryable oluşturulduğunda veya değiştirildiğinde, veritabanına hiçbir sorgu gönderilmez. IQueryableNesne bir koleksiyona dönüştürülene kadar sorgu yürütülmez. IQueryable , gibi bir yöntemi çağırarak bir koleksiyona dönüştürülür ToListAsync . Bu nedenle, IQueryable kod, aşağıdaki deyime kadar yürütülemeyen tek bir sorgu ile sonuçlanır:

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

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

Öğrenciler/Index. cshtml içindeki kodu 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:

  • LastNameVe sütun başlıklarına köprüler ekler EnrollmentDate .
  • , Ve içindeki bilgileri NameSort kullanarak DateSort geçerli sıralama düzeni değerleriyle köprüler ayarlar.

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

  • Uygulamayı çalıştırın ve öğrenciler sekmesini seçin.
  • Son ad' a tıklayın.
  • Kayıt tarihi' ne tıklayın.

Kodu daha iyi anlamak için:

  • Öğrenciler/Index. cshtml. cs dosyasında, üzerinde bir kesme noktası ayarlayın switch (sortOrder) .
  • Ve için bir izleme NameSort ekleyin DateSort .
  • Öğrenciler/Index. cshtml'de, üzerinde bir kesme noktası ayarlayın @Html.DisplayNameFor(model => model.Student[0].LastName) .

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

Öğrenciler dizin sayfasına bir arama kutusu ekleyin

Öğrenciler dizin sayfasına filtre eklemek için:

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

Dizin yöntemine filtreleme işlevi ekleme

Öğrenciler/Index. cshtml. cs ' i 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:

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

Note: Yukarıdaki kod, Where metodu bir nesne üzerinde çağırır IQueryable ve filtre sunucuda işlenir. Bazı senaryolarda, uygulama Where bir bellek içi koleksiyonda bir genişletme yöntemi olarak yöntemi çağırıyor olabilir. Örneğin, _context.Students EF Core 'den DbSet bir koleksiyonu döndüren bir depo yöntemine yapılan değişiklikleri varsayın IEnumerable . Sonuç normalde aynı olur, ancak bazı durumlarda farklı olabilir.

örneğin, uygulamasının .NET Framework uygulanması Contains varsayılan olarak büyük/küçük harfe duyarlı bir karşılaştırma gerçekleştirir. SQL Server, Contains büyük/küçük harf duyarlılığı SQL Server örneğinin harmanlama ayarına göre belirlenir. SQL Server varsayılan olarak büyük/küçük harfe duyarlı değildir. ToUpper testi açık 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, kodun kullanım için değişiklik yaptığı durumlarda sonuçların büyük/küçük harf duyarsız olmasını sağlar IEnumerable . ContainsBir koleksiyon üzerinde çağrıldığında IEnumerable .NET Core uygulamasını kullanır. ContainsBir nesne üzerinde çağrıldığında IQueryable , veritabanı uygulamasını kullanır. IEnumerableBir depodan döndürmek önemli bir performans cezasına sahip olabilir:

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

Çağırmak için bir performans cezası vardır ToUpper . ToUpperKod, TSQL Select IFADESININ WHERE yan tümcesine bir işlev ekler. Eklenen işlev, iyileştiricinin bir dizin kullanmasını önler. SQL, büyük/küçük harfe duyarsız olarak yüklendiği için, gerekli olmadığında çağrının önüne geçmek en iyisidir ToUpper .

Öğrenci dizin sayfasına bir arama kutusu ekleyin

Sayfalar/öğrenciler/Index. cshtml' de, bir arama düğmesi ve asi grafik 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, <form> Arama metin kutusu ve düğme eklemek için etiket yardımcısını kullanır. Varsayılan olarak, <form> etiket Yardımcısı form verilerini BIR gönderiyle gönderir. POST ile parametreler, URL 'de değil HTTP ileti gövdesine geçirilir. HTTP GET kullanıldığında, form verileri URL 'ye sorgu dizeleri olarak geçirilir. Verilerin sorgu dizelerine geçirilmesi, kullanıcıların URL 'YI yer işaretine eklemesini sağlar. W3C yönergeleri , eylem bir güncelleştirme ile SONUÇLANMAZSA, Get 'in kullanılması önerilir.

Uygulamayı test etme:

  • Öğ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

Sayfa yer işaretiyle, yer işareti sayfanın URL 'sini ve SearchString sorgu dizesini içerir. method="get" form Etiketi, sorgu dizesinin oluşturulmasına neden oldu.

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

Öğrenciler dizin sayfasına sayfalama işlevselliği ekleme

Bu bölümde, PaginatedList sayfalama desteği için bir sınıf oluşturulur. PaginatedListSınıfı, Skip Take tablodaki tüm satırları almak yerine, sunucudaki verileri filtrelemek için ve deyimlerini kullanır. Aşağıdaki çizimde sayfalama düğmeleri gösterilmektedir.

Sayfa bağlantılarıyla öğrenciler Dizin sayfası

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

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
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (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 kodda yer alan yöntemi sayfa boyutunu ve sayfa numarasını alır ve uygun ve Skip Take deyimlerini 'ye IQueryable uygular. üzerinde ToListAsync çağrıldı IQueryable mı, yalnızca istenen sayfayı içeren bir Liste döndürür. ve HasPreviousPage özellikleri, HasNextPage Önceki ve Sonraki disk belleği düğmelerini etkinleştirmek veya devre dışı bırakmak için kullanılır.

CreateAsyncyöntemi, oluşturmak için PaginatedList<T> kullanılır. Oluşturucu nesneyi PaginatedList<T> oluşturamadı, oluşturucular zaman uyumsuz kod çalıştıramadı.

Index yöntemine sayfalama işlevi ekleme

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

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

Students/Index.cshtml.cs dosyasını 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 sortOrder ve 'i currentFilter yöntem imzasını ekler.

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

Aşağıdakiler olduğunda 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ıklanan sayfa dizini değişkeni, görüntü verilecek sayfa numarasını içerir.

CurrentSort sayfayı Razor geçerli sıralama düzeniyle birlikte sağlar. Sayfalama sırasında sıralama sıralama düzeni tutmak için geçerli sıralama düzeni sayfalama bağlantılarına dahil edilecektir.

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

  • Sayfalama sırasında filtre ayarlarını korumak için sayfalama bağlantılarına dahil edilecektir.
  • Sayfa yeniden oynatıldı olduğunda metin kutusuna geri yüklenecektir.

Sayfalama sırasında arama dizesi değiştirilirse sayfa 1'e sıfırlanır. Yeni filtre farklı verilerin görüntülenmeye neden olması nedeniyle sayfanın 1'e sıfır olması gerekir. Bir arama değeri girilir ve Gönder seçildiğinde:

  • Arama dizesi değiştirilir.
  • parametresi searchString 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ünde tek bir öğrenci sayfasına dönüştürür. Bu tek öğrenci sayfası Sayfaya Razor geçirildi.

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

içinde iki soru işareti PaginatedList.CreateAsync null-coalescing işlecini temsil ediyor. Null-birleştirilebilir bir tür için varsayılan bir değer tanımlar. ifadesi, (pageIndex ?? 1) değerine pageIndex sahipse değerinin dönüş anlamına gelir. pageIndexbir değeri yoksa 1 değerini dönüş.

Students/Index.cshtml içinde iş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ı, geçerli arama dizesini yöntemine iletip kullanıcının filtre sonuçları içinde sıralaması OnGetAsync için 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ışır olduğundan emin olmak için farklı sıralama sıralarında sayfalama bağlantılarına tıklayın.
  • Disk belleğinin sıralama ve filtreleme ile düzgün çalıştığını doğrulamak için bir arama dizesi girin ve disk belleğini deneyin.

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

Kodu daha iyi anlamak için:

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

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

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

Bu adımda Pages/About.cshtml, her kayıt tarihi için kaç öğrenci kaydolan öğrenci olduğunu görüntülenecek şekilde güncelleştirilir. Güncelleştirme, gruplama 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

Models 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'de web şablonları Hakkında sayfasını içermez. ASP.NET Core 2.2 kullanıyorsanız Hakkında Sayfasını Razor oluşturun.

Pages/About.cshtml.cs dosyasını 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 gruplar, her gruptaki varlık sayısını hesaplar ve sonuçları bir görünüm EnrollmentDateGroup modeli nesneleri koleksiyonunda depolar.

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

Pages/About.cshtml dosyasındaki 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.

Çöze olmadığınız sorunlar varsa, 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.