Bagikan melalui


Bagian 7, tambahkan pencarian ke aplikasi ASP.NET Core MVC

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Oleh Rick Anderson

Di bagian ini, Anda menambahkan kemampuan pencarian ke Index metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama.

Perbarui metode yang Index ditemukan di dalam Controllers/MoviesController.cs dengan kode berikut:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Baris berikut dalam Index metode tindakan membuat kueri LINQ untuk memilih film:

var movies = from m in _context.Movie
             select m;

Kueri hanya ditentukan pada saat ini, kueri belum dijalankan terhadap database.

searchString Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian:

if (!String.IsNullOrEmpty(searchString))
{
    movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}

Kode s => s.Title!.ToUpper().Contains(searchString.ToUpper()) di atas adalah Ekspresi Lambda. Lambda digunakan dalam kueri LINQ berbasis metode sebagai argumen ke metode operator kueri standar seperti Where metode atau Contains (digunakan dalam kode di atas). Kueri LINQ tidak dijalankan saat didefinisikan atau saat dimodifikasi dengan memanggil metode seperti Where, , Containsatau OrderBy. Sebaliknya, eksekusi kueri ditangguhkan. Itu berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToListAsync metode dipanggil. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.

Catatan

Metode Contains ini dijalankan pada database, bukan dalam kode C#. Sensitivitas kasus pada kueri bergantung pada database dan kolaterasi. Di SQL Server, Contains peta ke SQL LIKE, yang tidak peka huruf besar/kecil. SQLite dengan kolasasi default adalah campuran peka huruf besar/kecil dan peka huruf besar/kecil,tergantung pada kueri. Untuk informasi tentang membuat kueri SQLite yang tidak peka huruf besar/kecil, lihat yang berikut ini:

Buka /Movies/Index. Tambahkan string kueri seperti ?searchString=Ghost ke URL. Film yang difilter ditampilkan.

Tampilan indeks

Jika Anda mengubah tanda tangan Index metode agar memiliki parameter bernama id, id parameter akan cocok dengan tempat penampung opsional {id} untuk rute default yang diatur dalam Program.cs.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Ubah parameter menjadi id dan ubah semua kemunculan menjadi searchStringid.

Metode Index sebelumnya:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Metode yang diperbarui Index dengan id parameter:

public async Task<IActionResult> Index(string id)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(id))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.

Tampilan indeks dengan kata hantu ditambahkan ke Url dan daftar film yang dikembalikan dari dua film, Ghostbusters dan Ghostbusters 2

Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan elemen UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan Index metode untuk menguji cara melewati parameter terikat ID rute, ubah kembali sehingga dibutuhkan parameter bernama searchString:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Views/Movies/Index.cshtml Buka file, dan tambahkan markup yang <form> disorot di bawah ini:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Tag HTML <form> menggunakan Pembantu Tag Formulir, jadi saat Anda mengirimkan formulir, string filter diposting ke Index tindakan pengontrol film. Simpan perubahan Anda lalu uji filter.

Tampilan indeks dengan kata hantu yang ditik ke dalam kotak teks Filter judul

Tidak ada [HttpPost] kelebihan metode seperti yang Index Anda harapkan. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.

Anda dapat menambahkan metode berikut [HttpPost] Index .

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Parameter notUsed digunakan untuk membuat kelebihan beban untuk metode .Index Kita akan membahasnya nanti dalam tutorial.

Jika Anda menambahkan metode ini, pemanggil tindakan akan cocok dengan [HttpPost] Index metode , dan [HttpPost] Index metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.

Jendela browser dengan respons aplikasi Dari Indeks HttpPost: filter pada hantu

Namun, bahkan jika Anda menambahkan versi [HttpPost] metode iniIndex, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:{PORT}/Movies/Index) -- tidak ada informasi pencarian di URL. Informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Anda dapat memverifikasi bahwa dengan browser Alat pengembang atau alat Fiddler yang sangat baik.

Gambar berikut menunjukkan browser Chrome Alat pengembang dengan tab Jaringan dan Header dipilih:

Tab Jaringan dan Header dari Alat Pengembang browser Chrome memperlihatkan isi permintaan dengan nilai searchString hantu

Tab Jaringan dan Payload dipilih untuk melihat data formulir:

Tab Jaringan dan Payload dari Alat Pengembang browser Chrome memperlihatkan data formulir

Anda dapat melihat parameter pencarian dan token XSRF di isi permintaan. Catatan, seperti yang disebutkan dalam tutorial sebelumnya, Form Tag Helper menghasilkan token antiforgery XSRF . Kami tidak memodifikasi data, jadi kami tidak perlu memvalidasi token dalam metode pengontrol.

Karena parameter pencarian berada di isi permintaan dan bukan URL, Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau berbagi dengan orang lain. Perbaiki ini dengan menentukan permintaan harus berada HTTP GET di tag yang formViews/Movies/Index.cshtml ditemukan dalam file.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index" method="get">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet Index metode tindakan, bahkan jika Anda memiliki HttpPost Index metode .

Jendela browser memperlihatkan searchString=ghost di Url dan film yang dikembalikan, Ghostbusters dan Ghostbusters 2, berisi kata ghost

Tambahkan Pencarian menurut genre

Tambahkan kelas berikut MovieGenreViewModel ke folder Model :

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models;

public class MovieGenreViewModel
{
    public List<Movie>? Movies { get; set; }
    public SelectList? Genres { get; set; }
    public string? MovieGenre { get; set; }
    public string? SearchString { get; set; }
}

Model tampilan genre film akan berisi:

  • Daftar film.
  • Yang SelectList berisi daftar genre. Ini memungkinkan pengguna untuk memilih genre dari daftar.
  • MovieGenre, yang berisi genre yang dipilih.
  • SearchString, yang berisi pengguna teks yang dimasukkan dalam kotak teks pencarian.

Index Ganti metode dengan MoviesController.cs kode berikut:

// GET: Movies
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    var movies = from m in _context.Movie
                 select m;

    if (!string.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    if (!string.IsNullOrEmpty(movieGenre))
    {
        movies = movies.Where(x => x.Genre == movieGenre);
    }

    var movieGenreVM = new MovieGenreViewModel
    {
        Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
        Movies = await movies.ToListAsync()
    };

    return View(movieGenreVM);
}

Kode berikut adalah LINQ kueri yang mengambil semua genre dari database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

Genre SelectList dibuat dengan memproyeksikan genre yang berbeda (kami tidak ingin daftar pilihan kami memiliki genre duplikat).

Saat pengguna mencari item, nilai pencarian dipertahankan di kotak pencarian.

Menambahkan pencarian menurut genre ke tampilan Indeks

Pembaruan Index.cshtml ditemukan di Tampilan/Film/ sebagai berikut:

@model MvcMovie.Models.MovieGenreViewModel

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>

        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>

        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Movies!)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </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>

Periksa ekspresi lambda yang digunakan dalam Pembantu HTML berikut:

@Html.DisplayNameFor(model => model.Movies![0].Title)

Dalam kode sebelumnya, Pembantu DisplayNameFor HTML memeriksa properti yang Title direferensikan dalam ekspresi lambda untuk menentukan nama tampilan. Karena ekspresi lambda diperiksa daripada dievaluasi, Anda tidak menerima pelanggaran akses saat model, , model.Moviesatau model.Movies[0]null atau kosong. Saat ekspresi lambda dievaluasi (misalnya, @Html.DisplayFor(modelItem => item.Title)), nilai properti model dievaluasi. Setelahnya adalah operator null-forgiving, yang digunakan untuk mendeklarasikan bahwa ! itu bukan null.model.MoviesMovies

Uji aplikasi dengan mencari berdasarkan genre, berdasarkan judul film, dan oleh keduanya:

Jendela browser memperlihatkan hasil https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

Di bagian ini, Anda menambahkan kemampuan pencarian ke Index metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama.

Perbarui metode yang Index ditemukan di dalam Controllers/MoviesController.cs dengan kode berikut:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Baris berikut dalam Index metode tindakan membuat kueri LINQ untuk memilih film:

var movies = from m in _context.Movie
             select m;

Kueri hanya ditentukan pada saat ini, kueri belum dijalankan terhadap database.

searchString Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian:

if (!String.IsNullOrEmpty(searchString))
{
    movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}

Kode s => s.Title!.ToUpper().Contains(searchString.ToUpper()) di atas adalah Ekspresi Lambda. Lambda digunakan dalam kueri LINQ berbasis metode sebagai argumen ke metode operator kueri standar seperti Where metode atau Contains (digunakan dalam kode di atas). Kueri LINQ tidak dijalankan saat didefinisikan atau saat dimodifikasi dengan memanggil metode seperti Where, , Containsatau OrderBy. Sebaliknya, eksekusi kueri ditangguhkan. Itu berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToListAsync metode dipanggil. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.

Catatan

Metode Contains ini dijalankan pada database, bukan dalam kode C#. Sensitivitas kasus pada kueri bergantung pada database dan kolaterasi. Di SQL Server, Contains peta ke SQL LIKE, yang tidak peka huruf besar/kecil. SQLite dengan kolasasi default adalah campuran peka huruf besar/kecil dan peka huruf besar/kecil,tergantung pada kueri. Untuk informasi tentang membuat kueri SQLite yang tidak peka huruf besar/kecil, lihat yang berikut ini:

Buka /Movies/Index. Tambahkan string kueri seperti ?searchString=Ghost ke URL. Film yang difilter ditampilkan.

Tampilan indeks

Jika Anda mengubah tanda tangan Index metode agar memiliki parameter bernama id, id parameter akan cocok dengan tempat penampung opsional {id} untuk rute default yang diatur dalam Program.cs.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Ubah parameter menjadi id dan ubah semua kemunculan menjadi searchStringid.

Metode Index sebelumnya:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Metode yang diperbarui Index dengan id parameter:

public async Task<IActionResult> Index(string id)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(id))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.

Tampilan indeks dengan kata hantu ditambahkan ke Url dan daftar film yang dikembalikan dari dua film, Ghostbusters dan Ghostbusters 2

Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan elemen UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan Index metode untuk menguji cara melewati parameter terikat ID rute, ubah kembali sehingga dibutuhkan parameter bernama searchString:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Views/Movies/Index.cshtml Buka file, dan tambahkan markup yang <form> disorot di bawah ini:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Tag HTML <form> menggunakan Pembantu Tag Formulir, jadi saat Anda mengirimkan formulir, string filter diposting ke Index tindakan pengontrol film. Simpan perubahan Anda lalu uji filter.

Tampilan indeks dengan kata hantu yang ditik ke dalam kotak teks Filter judul

Tidak ada [HttpPost] kelebihan metode seperti yang Index Anda harapkan. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.

Anda dapat menambahkan metode berikut [HttpPost] Index .

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Parameter notUsed digunakan untuk membuat kelebihan beban untuk metode .Index Kita akan membahasnya nanti dalam tutorial.

Jika Anda menambahkan metode ini, pemanggil tindakan akan cocok dengan [HttpPost] Index metode , dan [HttpPost] Index metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.

Jendela browser dengan respons aplikasi Dari Indeks HttpPost: filter pada hantu

Namun, bahkan jika Anda menambahkan versi [HttpPost] metode iniIndex, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:{PORT}/Movies/Index) -- tidak ada informasi pencarian di URL. Informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Anda dapat memverifikasi bahwa dengan browser Alat pengembang atau alat Fiddler yang sangat baik. Gambar di bawah ini menunjukkan browser Chrome Alat pengembang:

Tab Jaringan Alat Pengembang Microsoft Edge memperlihatkan isi permintaan dengan nilai searchString hantu

Anda dapat melihat parameter pencarian dan token XSRF di isi permintaan. Catatan, seperti yang disebutkan dalam tutorial sebelumnya, Form Tag Helper menghasilkan token antiforgery XSRF . Kami tidak memodifikasi data, jadi kami tidak perlu memvalidasi token dalam metode pengontrol.

Karena parameter pencarian berada di isi permintaan dan bukan URL, Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau berbagi dengan orang lain. Perbaiki ini dengan menentukan permintaan harus HTTP GET ditemukan dalam Views/Movies/Index.cshtml file.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index" method="get">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet Index metode tindakan, bahkan jika Anda memiliki HttpPost Index metode .

Jendela browser memperlihatkan searchString=ghost di Url dan film yang dikembalikan, Ghostbusters dan Ghostbusters 2, berisi kata ghost

Markup berikut menunjukkan perubahan pada form tag:

<form asp-controller="Movies" asp-action="Index" method="get">

Tambahkan Pencarian menurut genre

Tambahkan kelas berikut MovieGenreViewModel ke folder Model :

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models;

public class MovieGenreViewModel
{
    public List<Movie>? Movies { get; set; }
    public SelectList? Genres { get; set; }
    public string? MovieGenre { get; set; }
    public string? SearchString { get; set; }
}

Model tampilan genre film akan berisi:

  • Daftar film.
  • Yang SelectList berisi daftar genre. Ini memungkinkan pengguna untuk memilih genre dari daftar.
  • MovieGenre, yang berisi genre yang dipilih.
  • SearchString, yang berisi pengguna teks yang dimasukkan dalam kotak teks pencarian.

Index Ganti metode dengan MoviesController.cs kode berikut:

// GET: Movies
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    var movies = from m in _context.Movie
                 select m;

    if (!string.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    if (!string.IsNullOrEmpty(movieGenre))
    {
        movies = movies.Where(x => x.Genre == movieGenre);
    }

    var movieGenreVM = new MovieGenreViewModel
    {
        Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
        Movies = await movies.ToListAsync()
    };

    return View(movieGenreVM);
}

Kode berikut adalah LINQ kueri yang mengambil semua genre dari database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

Genre SelectList dibuat dengan memproyeksikan genre yang berbeda (kami tidak ingin daftar pilihan kami memiliki genre duplikat).

Saat pengguna mencari item, nilai pencarian dipertahankan di kotak pencarian.

Menambahkan pencarian menurut genre ke tampilan Indeks

Pembaruan Index.cshtml ditemukan di Tampilan/Film/ sebagai berikut:

@model MvcMovie.Models.MovieGenreViewModel

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>

        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>

        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Movies!)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </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>

Periksa ekspresi lambda yang digunakan dalam Pembantu HTML berikut:

@Html.DisplayNameFor(model => model.Movies![0].Title)

Dalam kode sebelumnya, Pembantu DisplayNameFor HTML memeriksa properti yang Title direferensikan dalam ekspresi lambda untuk menentukan nama tampilan. Karena ekspresi lambda diperiksa daripada dievaluasi, Anda tidak menerima pelanggaran akses saat model, , model.Moviesatau model.Movies[0]null atau kosong. Saat ekspresi lambda dievaluasi (misalnya, @Html.DisplayFor(modelItem => item.Title)), nilai properti model dievaluasi. Setelahnya adalah operator null-forgiving, yang digunakan untuk mendeklarasikan bahwa ! itu bukan null.model.MoviesMovies

Uji aplikasi dengan mencari berdasarkan genre, berdasarkan judul film, dan oleh keduanya:

Jendela browser memperlihatkan hasil https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

Di bagian ini, Anda menambahkan kemampuan pencarian ke Index metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama.

Perbarui metode yang Index ditemukan di dalam Controllers/MoviesController.cs dengan kode berikut:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Baris berikut dalam Index metode tindakan membuat kueri LINQ untuk memilih film:

var movies = from m in _context.Movie
             select m;

Kueri hanya ditentukan pada saat ini, kueri belum dijalankan terhadap database.

searchString Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian:

if (!String.IsNullOrEmpty(searchString))
{
    movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}

Kode s => s.Title!.ToUpper().Contains(searchString.ToUpper()) di atas adalah Ekspresi Lambda. Lambda digunakan dalam kueri LINQ berbasis metode sebagai argumen ke metode operator kueri standar seperti Where metode atau Contains (digunakan dalam kode di atas). Kueri LINQ tidak dijalankan saat didefinisikan atau saat dimodifikasi dengan memanggil metode seperti Where, , Containsatau OrderBy. Sebaliknya, eksekusi kueri ditangguhkan. Itu berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToListAsync metode dipanggil. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.

Catatan

Metode Contains ini dijalankan pada database, bukan dalam kode C#. Sensitivitas kasus pada kueri bergantung pada database dan kolaterasi. Di SQL Server, Contains peta ke SQL LIKE, yang tidak peka huruf besar/kecil. SQLite dengan kolasasi default adalah campuran peka huruf besar/kecil dan peka huruf besar/kecil,tergantung pada kueri. Untuk informasi tentang membuat kueri SQLite yang tidak peka huruf besar/kecil, lihat yang berikut ini:

Buka /Movies/Index. Tambahkan string kueri seperti ?searchString=Ghost ke URL. Film yang difilter ditampilkan.

Tampilan indeks

Jika Anda mengubah tanda tangan Index metode agar memiliki parameter bernama id, id parameter akan cocok dengan tempat penampung opsional {id} untuk rute default yang diatur dalam Program.cs.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Ubah parameter menjadi id dan ubah semua kemunculan menjadi searchStringid.

Metode Index sebelumnya:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Metode yang diperbarui Index dengan id parameter:

public async Task<IActionResult> Index(string id)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(id))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.

Tampilan indeks dengan kata hantu ditambahkan ke Url dan daftar film yang dikembalikan dari dua film, Ghostbusters dan Ghostbusters 2

Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan elemen UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan Index metode untuk menguji cara melewati parameter terikat ID rute, ubah kembali sehingga dibutuhkan parameter bernama searchString:

public async Task<IActionResult> Index(string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    var movies = from m in _context.Movie
                select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    return View(await movies.ToListAsync());
}

Views/Movies/Index.cshtml Buka file, dan tambahkan markup yang <form> disorot di bawah ini:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Tag HTML <form> menggunakan Pembantu Tag Formulir, jadi saat Anda mengirimkan formulir, string filter diposting ke Index tindakan pengontrol film. Simpan perubahan Anda lalu uji filter.

Tampilan indeks dengan kata hantu yang ditik ke dalam kotak teks Filter judul

Tidak ada [HttpPost] kelebihan metode seperti yang Index Anda harapkan. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.

Anda dapat menambahkan metode berikut [HttpPost] Index .

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Parameter notUsed digunakan untuk membuat kelebihan beban untuk metode .Index Kita akan membahasnya nanti dalam tutorial.

Jika Anda menambahkan metode ini, pemanggil tindakan akan cocok dengan [HttpPost] Index metode , dan [HttpPost] Index metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.

Jendela browser dengan respons aplikasi Dari Indeks HttpPost: filter pada hantu

Namun, bahkan jika Anda menambahkan versi [HttpPost] metode iniIndex, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:{PORT}/Movies/Index) -- tidak ada informasi pencarian di URL. Informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Anda dapat memverifikasi bahwa dengan browser Alat pengembang atau alat Fiddler yang sangat baik. Gambar di bawah ini menunjukkan browser Chrome Alat pengembang:

Tab Jaringan Alat Pengembang Microsoft Edge memperlihatkan isi permintaan dengan nilai searchString hantu

Anda dapat melihat parameter pencarian dan token XSRF di isi permintaan. Catatan, seperti yang disebutkan dalam tutorial sebelumnya, Form Tag Helper menghasilkan token antiforgery XSRF . Kami tidak memodifikasi data, jadi kami tidak perlu memvalidasi token dalam metode pengontrol.

Karena parameter pencarian berada di isi permintaan dan bukan URL, Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau berbagi dengan orang lain. Perbaiki ini dengan menentukan permintaan harus HTTP GET ditemukan dalam Views/Movies/Index.cshtml file.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index" method="get">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet Index metode tindakan, bahkan jika Anda memiliki HttpPost Index metode .

Jendela browser memperlihatkan searchString=ghost di Url dan film yang dikembalikan, Ghostbusters dan Ghostbusters 2, berisi kata ghost

Markup berikut menunjukkan perubahan pada form tag:

<form asp-controller="Movies" asp-action="Index" method="get">

Tambahkan Pencarian menurut genre

Tambahkan kelas berikut MovieGenreViewModel ke folder Model :

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models;

public class MovieGenreViewModel
{
    public List<Movie>? Movies { get; set; }
    public SelectList? Genres { get; set; }
    public string? MovieGenre { get; set; }
    public string? SearchString { get; set; }
}

Model tampilan genre film akan berisi:

  • Daftar film.
  • Yang SelectList berisi daftar genre. Ini memungkinkan pengguna untuk memilih genre dari daftar.
  • MovieGenre, yang berisi genre yang dipilih.
  • SearchString, yang berisi pengguna teks yang dimasukkan dalam kotak teks pencarian.

Index Ganti metode dengan MoviesController.cs kode berikut:

// GET: Movies
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
    if (_context.Movie == null)
    {
        return Problem("Entity set 'MvcMovieContext.Movie'  is null.");
    }

    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    var movies = from m in _context.Movie
                 select m;

    if (!string.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
    }

    if (!string.IsNullOrEmpty(movieGenre))
    {
        movies = movies.Where(x => x.Genre == movieGenre);
    }

    var movieGenreVM = new MovieGenreViewModel
    {
        Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
        Movies = await movies.ToListAsync()
    };

    return View(movieGenreVM);
}

Kode berikut adalah LINQ kueri yang mengambil semua genre dari database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

Genre SelectList dibuat dengan memproyeksikan genre yang berbeda (kami tidak ingin daftar pilihan kami memiliki genre duplikat).

Saat pengguna mencari item, nilai pencarian dipertahankan di kotak pencarian.

Menambahkan pencarian menurut genre ke tampilan Indeks

Pembaruan Index.cshtml ditemukan di Tampilan/Film/ sebagai berikut:

@model MvcMovie.Models.MovieGenreViewModel

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>

        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>

        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies![0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Movies!)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </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>

Periksa ekspresi lambda yang digunakan dalam Pembantu HTML berikut:

@Html.DisplayNameFor(model => model.Movies![0].Title)

Dalam kode sebelumnya, Pembantu DisplayNameFor HTML memeriksa properti yang Title direferensikan dalam ekspresi lambda untuk menentukan nama tampilan. Karena ekspresi lambda diperiksa daripada dievaluasi, Anda tidak menerima pelanggaran akses saat model, , model.Moviesatau model.Movies[0]null atau kosong. Saat ekspresi lambda dievaluasi (misalnya, @Html.DisplayFor(modelItem => item.Title)), nilai properti model dievaluasi. Setelahnya adalah operator null-forgiving, yang digunakan untuk mendeklarasikan bahwa ! itu bukan null.model.MoviesMovies

Uji aplikasi dengan mencari berdasarkan genre, berdasarkan judul film, dan oleh keduanya:

Jendela browser memperlihatkan hasil https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

Di bagian ini, Anda menambahkan kemampuan pencarian ke Index metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama.

Perbarui metode yang Index ditemukan di dalam Controllers/MoviesController.cs dengan kode berikut:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Baris Index pertama metode tindakan membuat kueri LINQ untuk memilih film:

var movies = from m in _context.Movie
             select m;

Kueri hanya ditentukan pada saat ini, kueri belum dijalankan terhadap database.

searchString Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian:

if (!String.IsNullOrEmpty(searchString))
{
    movies = movies.Where(s => s.Title!.Contains(searchString));
}

Kode s => s.Title!.Contains(searchString) di atas adalah Ekspresi Lambda. Lambda digunakan dalam kueri LINQ berbasis metode sebagai argumen ke metode operator kueri standar seperti Where metode atau Contains (digunakan dalam kode di atas). Kueri LINQ tidak dijalankan saat didefinisikan atau saat dimodifikasi dengan memanggil metode seperti Where, , Containsatau OrderBy. Sebaliknya, eksekusi kueri ditangguhkan. Itu berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToListAsync metode dipanggil. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.

Catatan: Metode Contains dijalankan pada database, bukan dalam kode c# yang ditunjukkan di atas. Sensitivitas kasus pada kueri bergantung pada database dan kolaterasi. Di SQL Server, Contains peta ke SQL LIKE, yang tidak peka huruf besar/kecil. Di SQLite, dengan kolate default, ini peka huruf besar/kecil.

Buka /Movies/Index. Tambahkan string kueri seperti ?searchString=Ghost ke URL. Film yang difilter ditampilkan.

Tampilan indeks

Jika Anda mengubah tanda tangan Index metode agar memiliki parameter bernama id, id parameter akan cocok dengan tempat penampung opsional {id} untuk rute default yang diatur dalam Program.cs.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Ubah parameter menjadi id dan ubah semua kemunculan menjadi searchStringid.

Metode Index sebelumnya:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Metode yang diperbarui Index dengan id parameter:

public async Task<IActionResult> Index(string id)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(id))
    {
        movies = movies.Where(s => s.Title!.Contains(id));
    }

    return View(await movies.ToListAsync());
}

Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.

Tampilan indeks dengan kata hantu ditambahkan ke Url dan daftar film yang dikembalikan dari dua film, Ghostbusters dan Ghostbusters 2

Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan elemen UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan Index metode untuk menguji cara melewati parameter terikat ID rute, ubah kembali sehingga dibutuhkan parameter bernama searchString:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Views/Movies/Index.cshtml Buka file, dan tambahkan markup yang <form> disorot di bawah ini:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h2>Index</h2>

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

<form asp-controller="Movies" asp-action="Index">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

Tag HTML <form> menggunakan Pembantu Tag Formulir, jadi saat Anda mengirimkan formulir, string filter diposting ke Index tindakan pengontrol film. Simpan perubahan Anda lalu uji filter.

Tampilan indeks dengan kata hantu yang ditik ke dalam kotak teks Filter judul

Tidak ada [HttpPost] kelebihan metode seperti yang Index Anda harapkan. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.

Anda dapat menambahkan metode berikut [HttpPost] Index .

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Parameter notUsed digunakan untuk membuat kelebihan beban untuk metode .Index Kita akan membahasnya nanti dalam tutorial.

Jika Anda menambahkan metode ini, pemanggil tindakan akan cocok dengan [HttpPost] Index metode , dan [HttpPost] Index metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.

Jendela browser dengan respons aplikasi Dari Indeks HttpPost: filter pada hantu

Namun, bahkan jika Anda menambahkan versi [HttpPost] metode iniIndex, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:{PORT}/Movies/Index) -- tidak ada informasi pencarian di URL. Informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Anda dapat memverifikasi bahwa dengan browser Alat pengembang atau alat Fiddler yang sangat baik. Gambar di bawah ini menunjukkan browser Chrome Alat pengembang:

Tab Jaringan Alat Pengembang Microsoft Edge memperlihatkan isi permintaan dengan nilai searchString hantu

Anda dapat melihat parameter pencarian dan token XSRF di isi permintaan. Catatan, seperti yang disebutkan dalam tutorial sebelumnya, Form Tag Helper menghasilkan token antiforgery XSRF . Kami tidak memodifikasi data, jadi kami tidak perlu memvalidasi token dalam metode pengontrol.

Karena parameter pencarian berada di isi permintaan dan bukan URL, Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau berbagi dengan orang lain. Perbaiki ini dengan menentukan permintaan harus HTTP GET ditemukan dalam Views/Movies/Index.cshtml file.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

<form asp-controller="Movies" asp-action="Index" method="get">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet Index metode tindakan, bahkan jika Anda memiliki HttpPost Index metode .

Jendela browser memperlihatkan searchString=ghost di Url dan film yang dikembalikan, Ghostbusters dan Ghostbusters 2, berisi kata ghost

Markup berikut menunjukkan perubahan pada form tag:

<form asp-controller="Movies" asp-action="Index" method="get">

Tambahkan Pencarian menurut genre

Tambahkan kelas berikut MovieGenreViewModel ke folder Model :

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models
{
    public class MovieGenreViewModel
    {
        public List<Movie>? Movies { get; set; }
        public SelectList? Genres { get; set; }
        public string? MovieGenre { get; set; }
        public string? SearchString { get; set; }
    }
}

Model tampilan genre film akan berisi:

  • Daftar film.
  • Yang SelectList berisi daftar genre. Ini memungkinkan pengguna untuk memilih genre dari daftar.
  • MovieGenre, yang berisi genre yang dipilih.
  • SearchString, yang berisi pengguna teks yang dimasukkan dalam kotak teks pencarian.

Index Ganti metode dengan MoviesController.cs kode berikut:

// GET: Movies
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    var movies = from m in _context.Movie
                 select m;

    if (!string.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title!.Contains(searchString));
    }

    if (!string.IsNullOrEmpty(movieGenre))
    {
        movies = movies.Where(x => x.Genre == movieGenre);
    }

    var movieGenreVM = new MovieGenreViewModel
    {
        Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
        Movies = await movies.ToListAsync()
    };

    return View(movieGenreVM);
}

Kode berikut adalah LINQ kueri yang mengambil semua genre dari database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

Genre SelectList dibuat dengan memproyeksikan genre yang berbeda (kami tidak ingin daftar pilihan kami memiliki genre duplikat).

Saat pengguna mencari item, nilai pencarian dipertahankan di kotak pencarian.

Menambahkan pencarian menurut genre ke tampilan Indeks

Pembaruan Index.cshtml ditemukan di Tampilan/Film/ sebagai berikut:

@model MvcMovie.Models.MovieGenreViewModel

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>

        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>

        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Movies)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </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>

Periksa ekspresi lambda yang digunakan dalam Pembantu HTML berikut:

@Html.DisplayNameFor(model => model.Movies[0].Title)

Dalam kode sebelumnya, Pembantu DisplayNameFor HTML memeriksa properti yang Title direferensikan dalam ekspresi lambda untuk menentukan nama tampilan. Karena ekspresi lambda diperiksa daripada dievaluasi, Anda tidak menerima pelanggaran akses saat model, , model.Moviesatau model.Movies[0]null atau kosong. Saat ekspresi lambda dievaluasi (misalnya, @Html.DisplayFor(modelItem => item.Title)), nilai properti model dievaluasi.

Uji aplikasi dengan mencari berdasarkan genre, berdasarkan judul film, dan oleh keduanya:

Jendela browser memperlihatkan hasil https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

Di bagian ini, Anda menambahkan kemampuan pencarian ke Index metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama.

Perbarui metode yang Index ditemukan di dalam Controllers/MoviesController.cs dengan kode berikut:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Baris Index pertama metode tindakan membuat kueri LINQ untuk memilih film:

var movies = from m in _context.Movie
             select m;

Kueri hanya ditentukan pada saat ini, kueri belum dijalankan terhadap database.

searchString Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian:

if (!String.IsNullOrEmpty(searchString))
{
    movies = movies.Where(s => s.Title.Contains(searchString));
}

Kode s => s.Title.Contains() di atas adalah Ekspresi Lambda. Lambda digunakan dalam kueri LINQ berbasis metode sebagai argumen ke metode operator kueri standar seperti Where metode atau Contains (digunakan dalam kode di atas). Kueri LINQ tidak dijalankan saat didefinisikan atau saat dimodifikasi dengan memanggil metode seperti Where, , Containsatau OrderBy. Sebaliknya, eksekusi kueri ditangguhkan. Itu berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToListAsync metode dipanggil. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.

Catatan: Metode Contains dijalankan pada database, bukan dalam kode c# yang ditunjukkan di atas. Sensitivitas kasus pada kueri bergantung pada database dan kolaterasi. Di SQL Server, Contains peta ke SQL LIKE, yang tidak peka huruf besar/kecil. Di SQLite, dengan kolate default, ini peka huruf besar/kecil.

Buka /Movies/Index. Tambahkan string kueri seperti ?searchString=Ghost ke URL. Film yang difilter ditampilkan.

Tampilan indeks

Jika Anda mengubah tanda tangan Index metode agar memiliki parameter bernama id, id parameter akan cocok dengan tempat penampung opsional {id} untuk rute default yang diatur dalam Startup.cs.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

Ubah parameter ke id dan semua kemunculan searchString perubahan ke id.

Metode Index sebelumnya:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Metode yang diperbarui Index dengan id parameter:

public async Task<IActionResult> Index(string id)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(id))
    {
        movies = movies.Where(s => s.Title.Contains(id));
    }

    return View(await movies.ToListAsync());
}

Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.

Tampilan indeks dengan kata hantu ditambahkan ke Url dan daftar film yang dikembalikan dari dua film, Ghostbusters dan Ghostbusters 2

Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan elemen UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan Index metode untuk menguji cara melewati parameter terikat ID rute, ubah kembali sehingga dibutuhkan parameter bernama searchString:

public async Task<IActionResult> Index(string searchString)
{
    var movies = from m in _context.Movie
                 select m;

    if (!String.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title.Contains(searchString));
    }

    return View(await movies.ToListAsync());
}

Views/Movies/Index.cshtml Buka file, dan tambahkan markup yang <form> disorot di bawah ini:

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

<h2>Index</h2>

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

<form asp-controller="Movies" asp-action="Index">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>

Tag HTML <form> menggunakan Pembantu Tag Formulir, jadi saat Anda mengirimkan formulir, string filter diposting ke Index tindakan pengontrol film. Simpan perubahan Anda lalu uji filter.

Tampilan indeks dengan kata hantu yang ditik ke dalam kotak teks Filter judul

Tidak ada [HttpPost] kelebihan metode seperti yang Index Anda harapkan. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.

Anda dapat menambahkan metode berikut [HttpPost] Index .

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Parameter notUsed digunakan untuk membuat kelebihan beban untuk metode .Index Kita akan membahasnya nanti dalam tutorial.

Jika Anda menambahkan metode ini, pemanggil tindakan akan cocok dengan [HttpPost] Index metode , dan [HttpPost] Index metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.

Jendela browser dengan respons aplikasi Dari Indeks HttpPost: filter pada hantu

Namun, bahkan jika Anda menambahkan versi [HttpPost] metode iniIndex, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:{PORT}/Movies/Index) -- tidak ada informasi pencarian di URL. Informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Anda dapat memverifikasi bahwa dengan browser Alat pengembang atau alat Fiddler yang sangat baik. Gambar di bawah ini menunjukkan browser Chrome Alat pengembang:

Tab Jaringan Alat Pengembang Microsoft Edge memperlihatkan isi permintaan dengan nilai searchString hantu

Anda dapat melihat parameter pencarian dan token XSRF di isi permintaan. Catatan, seperti yang disebutkan dalam tutorial sebelumnya, Form Tag Helper menghasilkan token antiforgery XSRF . Kami tidak memodifikasi data, jadi kami tidak perlu memvalidasi token dalam metode pengontrol.

Karena parameter pencarian berada di isi permintaan dan bukan URL, Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau berbagi dengan orang lain. Perbaiki ini dengan menentukan permintaan harus HTTP GET ditemukan dalam Views/Movies/Index.cshtml file.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>
        <label>Title: <input type="text" name="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)

Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet Index metode tindakan, bahkan jika Anda memiliki HttpPost Index metode .

Jendela browser memperlihatkan searchString=ghost di Url dan film yang dikembalikan, Ghostbusters dan Ghostbusters 2, berisi kata ghost

Markup berikut menunjukkan perubahan pada form tag:

<form asp-controller="Movies" asp-action="Index" method="get">

Tambahkan Pencarian menurut genre

Tambahkan kelas berikut MovieGenreViewModel ke folder Model :

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models
{
    public class MovieGenreViewModel
    {
        public List<Movie> Movies { get; set; }
        public SelectList Genres { get; set; }
        public string MovieGenre { get; set; }
        public string SearchString { get; set; }
    }
}

Model tampilan genre film akan berisi:

  • Daftar film.
  • Yang SelectList berisi daftar genre. Ini memungkinkan pengguna untuk memilih genre dari daftar.
  • MovieGenre, yang berisi genre yang dipilih.
  • SearchString, yang berisi pengguna teks yang dimasukkan dalam kotak teks pencarian.

Index Ganti metode dengan MoviesController.cs kode berikut:

// GET: Movies
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;

    var movies = from m in _context.Movie
                 select m;

    if (!string.IsNullOrEmpty(searchString))
    {
        movies = movies.Where(s => s.Title.Contains(searchString));
    }

    if (!string.IsNullOrEmpty(movieGenre))
    {
        movies = movies.Where(x => x.Genre == movieGenre);
    }

    var movieGenreVM = new MovieGenreViewModel
    {
        Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
        Movies = await movies.ToListAsync()
    };

    return View(movieGenreVM);
}

Kode berikut adalah LINQ kueri yang mengambil semua genre dari database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

Genre SelectList dibuat dengan memproyeksikan genre yang berbeda (kami tidak ingin daftar pilihan kami memiliki genre duplikat).

Saat pengguna mencari item, nilai pencarian dipertahankan di kotak pencarian.

Menambahkan pencarian menurut genre ke tampilan Indeks

Pembaruan Index.cshtml ditemukan di Tampilan/Film/ sebagai berikut:

@model MvcMovie.Models.MovieGenreViewModel

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
    <p>

        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>

        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Movies)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </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>

Periksa ekspresi lambda yang digunakan dalam Pembantu HTML berikut:

@Html.DisplayNameFor(model => model.Movies[0].Title)

Dalam kode sebelumnya, Pembantu DisplayNameFor HTML memeriksa properti yang Title direferensikan dalam ekspresi lambda untuk menentukan nama tampilan. Karena ekspresi lambda diperiksa daripada dievaluasi, Anda tidak menerima pelanggaran akses saat model, , model.Moviesatau model.Movies[0]null atau kosong. Saat ekspresi lambda dievaluasi (misalnya, @Html.DisplayFor(modelItem => item.Title)), nilai properti model dievaluasi.

Uji aplikasi dengan mencari berdasarkan genre, berdasarkan judul film, dan oleh keduanya:

Jendela browser memperlihatkan hasil https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2