Öğretici: Sıralama, filtreleme ve sayfalama ekleme - ile MVC'yi ASP.NET EF Core

Önceki öğreticide, Öğrenci varlıkları için temel CRUD işlemleri için bir dizi web sayfası uyguladınız. Bu öğreticide, Öğrenci Dizini sayfasına sıralama, filtreleme ve sayfalama işlevleri ekleyeceksiniz. Ayrıca basit gruplandırmalar da yapacaksınız.

Aşağıdaki çizimde, işiniz bittiğinde sayfanın nasıl görüneceği gösterilmektedir. Sütun başlıkları, kullanıcının bu sütuna göre sıralamak için tıklayabileceği bağlantılardır. Sütun başlığına art arda tıklandığında artan ve azalan sıralama düzeni arasında geçiş yapar.

Students index page

Bu öğreticide şunları yaptınız:

  • Sütun sıralama bağlantıları ekleme
  • Arama kutusu ekleme
  • Öğrenci Dizinine sayfalama ekleme
  • Dizin yöntemine disk belleği ekleme
  • Sayfalama bağlantıları ekleme
  • Hakkında sayfası oluşturma

Ön koşullar

Öğrenci Dizini sayfasına sıralama eklemek için Öğrenciler denetleyicisinin Index yöntemini değiştirip Öğrenci Dizini görünümüne kod ekleyeceksiniz.

Dizin yöntemine sıralama İşlevselliği ekleme

içinde StudentsController.csyöntemini aşağıdaki kodla değiştirin Index :

public async Task<IActionResult> Index(string sortOrder)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    var students = from s in _context.Students
                   select s;
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

Bu kod, URL'deki sorgu dizesinden bir sortOrder parametre alır. Sorgu dizesi değeri, eylem yöntemine parametre olarak ASP.NET Core MVC tarafından sağlanır. parametresi , isteğe bağlı olarak alt çizgi ve azalan sırayı belirtmek için "desc" dizesinin ardından "Name" veya "Date" olan bir dize olacaktır. Varsayılan sıralama düzeni artan düzendedir.

Dizin sayfası ilk kez istenildiğinde sorgu dizesi yoktur. Öğrenciler soyadına göre artan düzende görüntülenir. Bu, deyimdeki switch fall-through case tarafından belirlenen varsayılan değerdir. Kullanıcı bir sütun başlığı köprüsüne tıkladığında, sorgu dizesinde uygun sortOrder değer sağlanır.

İki ViewData öğe (NameSortParm ve DateSortParm), görünüm tarafından sütun başlığı köprülerini uygun sorgu dizesi değerleriyle yapılandırmak için kullanılır.

public async Task<IActionResult> Index(string sortOrder)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    var students = from s in _context.Students
                   select s;
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

Bunlar üçüncül deyimlerdir. İlki, parametre null veya boşsa NameSortParm değerinin sortOrder "name_desc" olarak ayarlanması gerektiğini belirtir; aksi takdirde, boş bir dizeye ayarlanmalıdır. Bu iki deyim, görünümü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 değişken oluşturur, switch deyiminde değiştirir ve deyiminden ToListAsyncswitch sonra yöntemini çağırır. Değişkenleri oluşturduğunuzda ve değiştirdiğinizde IQueryable veritabanına hiçbir sorgu gönderilmez. Gibi bir yöntemi ToListAsyncçağırarak nesneyi bir koleksiyona dönüştürene IQueryable kadar sorgu yürütülemez. Bu nedenle, bu kod deyimine kadar yürütülmeyen tek bir sorguyla sonuçlanmıştır return View .

Bu kod çok sayıda sütunla ayrıntılı olabilir. Bu serideki son öğreticide, sütunun adını bir dize değişkenine geçirmenize olanak tanıyan kodun OrderBy nasıl yazıldığını gösterilmektedir.

içindeki kodu Views/Students/Index.cshtml, sütun başlığı köprüleri eklemek için aşağıdaki kodla değiştirin. Değiştirilen satırlar vurgulanır.

@model IEnumerable<ContosoUniversity.Models.Student>

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

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]">@Html.DisplayNameFor(model => model.LastName)</a>
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]">@Html.DisplayNameFor(model => model.EnrollmentDate)</a>
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <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-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Bu kod, uygun sorgu dizesi değerlerine sahip köprüler ayarlamak için özelliklerdeki ViewData bilgileri kullanır.

Uygulamayı çalıştırın, Öğrenciler sekmesini seçin ve sıralamanın çalıştığını doğrulamak için Soyadı ve Kayıt Tarihi sütun başlıklarına tıklayın.

Students index page in name order

Öğrenci Dizini sayfasına filtreleme eklemek için, görünüme bir metin kutusu ve gönder düğmesi ekleyip yönteminde Index ilgili değişiklikleri yapacaksınız. Metin kutusu, ad ve soyadı alanlarında aranacak bir dize girmenize olanak verir.

Index yöntemine filtreleme işlevi ekleme

içinde StudentsController.csyöntemini aşağıdaki kodla değiştirin Index (değişiklikler vurgulanır).

public async Task<IActionResult> Index(string sortOrder, string searchString)
{
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    ViewData["CurrentFilter"] = searchString;

    var students = from s in _context.Students
                   select s;
    if (!String.IsNullOrEmpty(searchString))
    {
        students = students.Where(s => s.LastName.Contains(searchString)
                               || s.FirstMidName.Contains(searchString));
    }
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:
            students = students.OrderBy(s => s.LastName);
            break;
    }
    return View(await students.AsNoTracking().ToListAsync());
}

yöntemine bir searchString parametre eklediniz Index . Arama dizesi değeri, Dizin görünümüne ekleyeceğiniz bir metin kutusundan alınır. AYRıCA LINQ deyimine, yalnızca ad veya soyadı arama dizesini içeren öğrencileri seçen bir where yan tümcesi eklediniz. where yan tümcesini ekleyen deyim yalnızca aranacak bir değer varsa yürütülür.

Dekont

Burada bir IQueryable nesnede Where yöntemini çağırırsınız ve filtre sunucuda işlenir. Bazı senaryolarda yöntemini bellek içi bir koleksiyonda uzantı yöntemi olarak çağırabilirsiniz Where . (Örneğin, EF yerine koleksiyon döndüren IEnumerable bir DbSet depo yöntemine başvurması için _context.Students başvuruyu olarak değiştirdiğinizden bahsedin.) Sonuç normalde aynı olur ancak bazı durumlarda farklı olabilir.

Örneğin, yönteminin Contains .NET Framework uygulaması varsayılan olarak büyük/küçük harfe duyarlı bir karşılaştırma gerçekleştirir, ancak SQL Server'da bu, SQL Server örneğinin harmanlama ayarı tarafından belirlenir. Bu ayar varsayılan olarak büyük/küçük harfe duyarlı değildir. Testi açıkça büyük/küçük harfe duyarsız hale getirmek için yöntemini çağırabilirsiniz ToUpper : Where(s => s.LastName.ToUpper(). Contains(searchString.ToUpper()). Bu, kodu daha sonra nesne yerine koleksiyon döndüren IEnumerable bir depo kullanacak şekilde değiştirirseniz sonuçların IQueryable aynı kalmasını sağlar. (Bir IEnumerable koleksiyonda Contains yöntemini çağırdığınızda .NET Framework uygulamasını alırsınız; bir IQueryable nesnede çağırdığınızda veritabanı sağlayıcısı uygulamasını alırsınız.) Ancak, bu çözüm için bir performans cezası vardır. Kod, ToUpper TSQL SELECT deyiminin WHERE yan tümcesine bir işlev koyar. Bu, iyileştiricinin bir dizin kullanmasını engeller. SQL'in büyük/küçük harfe duyarsız olarak yüklendiği göz önünde bulundurulduğunda, büyük/küçük harfe duyarlı bir veri deposuna geçirilene kadar koddan kaçınmak ToUpper en iyisidir.

Öğrenci Dizini Görünümüne Arama Kutusu Ekleme

içindeViews/Student/Index.cshtml, başlık, metin kutusu ve Ara düğmesi oluşturmak için tablo etiketini açmadan hemen önce vurgulanan kodu ekleyin.

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

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

<table class="table">

Bu kod, <form>arama metin kutusunu ve düğmesini eklemek için etiket yardımcısını kullanır. Varsayılan olarak, <form> etiket yardımcısı form verilerini POST ile gönderir; bu da parametrelerin URL'de sorgu dizesi olarak değil HTTP ileti gövdesine geçirildiği anlamına gelir. HTTP GET belirttiğinizde, form verileri URL'ye sorgu dizeleri olarak geçirilir ve bu da kullanıcıların URL'ye yer işareti eklemesini sağlar. W3C yönergeleri, eylem güncelleştirmeyle sonuçlanmadığında GET kullanmanızı önerir.

Uygulamayı çalıştırın, Öğrenciler sekmesini seçin, bir arama dizesi girin ve filtrelemenin çalıştığını doğrulamak için Ara'ya tıklayın.

Students index page with filtering

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

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

Bu sayfaya yer işareti eklerseniz, yer işaretini kullandığınızda filtrelenmiş listeyi alırsınız. Sorgu dizesinin oluşturulmasına form neden olan etikete ekleme method="get" işlemidir.

Bu aşamada, bir sütun başlığı sıralama bağlantısına tıklarsanız, Arama kutusuna girdiğiniz filtre değerini kaybedersiniz. Bunu sonraki bölümde düzelteceksiniz.

Öğrenci Dizinine sayfalama ekleme

Öğrenci Dizini sayfasına sayfalama eklemek için, her zaman tablonun tüm satırlarını almak yerine sunucudaki verileri filtrelemek için ve Take deyimlerini kullanan Skip bir PaginatedList sınıf oluşturacaksınız. Ardından yönteminde Index ek değişiklikler yapacak ve görünüme Index sayfalama düğmeleri ekleyeceksiniz. Aşağıdaki çizimde sayfalama düğmeleri gösterilmektedir.

Students index page with paging links

Proje klasöründe oluşturun PaginatedList.csve şablon kodunu aşağıdaki kodla değiştirin.

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

CreateAsync Bu 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 HasPreviousPageHasNextPage, Önceki ve Sonraki sayfalama düğmelerini etkinleştirmek veya devre dışı bırakmak için kullanılabilir.

CreateAsync Oluşturucular zaman uyumsuz kodu çalıştıramadığından, nesneyi oluşturmak PaginatedList<T> için oluşturucu yerine bir yöntem kullanılır.

Dizin yöntemine disk belleği ekleme

içinde StudentsController.csyöntemini aşağıdaki kodla değiştirin Index .

public async Task<IActionResult> Index(
    string sortOrder,
    string currentFilter,
    string searchString,
    int? pageNumber)
{
    ViewData["CurrentSort"] = sortOrder;
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";

    if (searchString != null)
    {
        pageNumber = 1;
    }
    else
    {
        searchString = currentFilter;
    }

    ViewData["CurrentFilter"] = searchString;

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

    int pageSize = 3;
    return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
}

Bu kod yöntem imzasına bir sayfa numarası parametresi, geçerli bir sıralama düzeni parametresi ve geçerli bir filtre parametresi ekler.

public async Task<IActionResult> Index(
    string sortOrder,
    string currentFilter,
    string searchString,
    int? pageNumber)

Sayfa ilk kez görüntülendiğinde veya kullanıcı bir sayfalama veya sıralama bağlantısına tıklamadıysa, tüm parametreler null olur. Sayfalama bağlantısına tıklanırsa, sayfa değişkeni görüntülenecek sayfa numarasını içerir.

ViewData CurrentSort adlı öğe, sayfalama sırasında sıralama düzeninin aynı tutulması için sayfalama bağlantılarına eklenmesi gerektiğinden geçerli sıralama düzenini içeren görünümü sağlar.

ViewData CurrentFilter adlı öğe, geçerli filtre dizesini içeren görünümü sağlar. Sayfalama sırasında filtre ayarlarını korumak için bu değerin sayfalama bağlantılarına eklenmesi ve sayfa yeniden görüntülendiğinde metin kutusuna geri yüklenmesi gerekir.

Sayfalama sırasında arama dizesi değiştirilirse, yeni filtre farklı verilerin görüntülenmesine neden olabileceğinden sayfanın 1'e sıfırlanması gerekir. Metin kutusuna bir değer girildiğinde ve Gönder düğmesine basıldığında arama dizesi değiştirilir. Bu durumda parametresi searchString null değildir.

if (searchString != null)
{
    pageNumber = 1;
}
else
{
    searchString = currentFilter;
}

Yöntemin Index sonunda 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. Tek bir öğrenci sayfası görünüme geçirilir.

return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));

PaginatedList.CreateAsync yöntemi bir sayfa numarası alır. İki 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; ifadesi (pageNumber ?? 1) , değeri varsa değerini pageNumber döndürme veya null ise pageNumber 1 döndürme anlamına gelir.

içinde Views/Students/Index.cshtml, mevcut kodu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

@model PaginatedList<ContosoUniversity.Models.Student>

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

<h2>Index</h2>

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

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

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Last Name</a>
            </th>
            <th>
                First Name
            </th>
            <th>
                <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a>
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <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-action="Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

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

<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex - 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @prevDisabled">
    Previous
</a>
<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex + 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @nextDisabled">
    Next
</a>

Sayfanın @model en üstündeki deyimi, görünümün artık nesne yerine List<T> nesne PaginatedList<T> aldığını belirtir.

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 denetleyiciye geçirmek için sorgu dizesini kullanır:

<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter ="@ViewData["CurrentFilter"]">Enrollment Date</a>

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

<a asp-action="Index"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex - 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-default @prevDisabled">
   Previous
</a>

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

Students index page with paging links

Disk belleğinin çalıştığından emin olmak için farklı sıralama düzenlerindeki sayfalama bağlantılarına tıklayın. Ardından bir arama dizesi girin ve disk belleğinin sıralama ve filtreleme ile de düzgün çalıştığını doğrulamak için yeniden sayfalama yapmayı deneyin.

Hakkında sayfası oluşturma

Contoso Üniversitesi web sitesinin Hakkında sayfasında, her kayıt tarihi için kaç öğrencinin kaydoldu olduğunu görüntülersiniz. Bunun için gruplar üzerinde gruplandırma ve basit hesaplamalar gerekir. Bunu başarmak için aşağıdakileri yapacaksınız:

  • Görünüme geçirmeniz gereken veriler için bir görünüm modeli sınıfı oluşturun.
  • Denetleyicide Home About yöntemini oluşturun.
  • Hakkında görünümünü oluşturun.

Görünüm modelini oluşturma

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

Yeni klasörde bir sınıf dosyası EnrollmentDateGroup.cs ekleyin ve şablon kodunu aşağıdaki kodla değiştirin:

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

Denetleyiciyi Home Değiştirme

içinde HomeController.csdosyasının en üstüne aşağıdaki using deyimlerini ekleyin:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.Extensions.Logging;

Sınıf için küme ayracı açıldıktan hemen sonra veritabanı bağlamı için bir sınıf değişkeni ekleyin ve ASP.NET Core DI'den bağlamın bir örneğini alın:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly SchoolContext _context;

    public HomeController(ILogger<HomeController> logger, SchoolContext context)
    {
        _logger = logger;
        _context = context;
    }

Aşağıdaki koda sahip bir About yöntem ekleyin:

public async Task<ActionResult> About()
{
    IQueryable<EnrollmentDateGroup> data = 
        from student in _context.Students
        group student by student.EnrollmentDate into dateGroup
        select new EnrollmentDateGroup()
        {
            EnrollmentDate = dateGroup.Key,
            StudentCount = dateGroup.Count()
        };
    return View(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 Görünümü Oluşturma

Aşağıdaki koda sahip bir Views/Home/About.cshtml dosya ekleyin:

@model IEnumerable<ContosoUniversity.Models.SchoolViewModels.EnrollmentDateGroup>

@{
    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)
    {
        <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.

Kodu alma

Tamamlanan uygulamayı indirin veya görüntüleyin.

Sonraki adımlar

Bu öğreticide şunları yaptınız:

  • Sütun sıralama bağlantıları eklendi
  • Arama kutusu eklendi
  • Öğrenci Dizinine disk belleği eklendi
  • Dizin yöntemine disk belleği eklendi
  • Sayfalama bağlantıları eklendi
  • Hakkında sayfası oluşturuldu

Geçişleri kullanarak veri modeli değişikliklerini işlemeyi öğrenmek için sonraki öğreticiye ilerleyin.