Sdílet prostřednictvím


Část 7: Přidání vyhledávání do aplikace ASP.NET Core MVC

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Autor: Rick Anderson

V této části přidáte funkci vyhledávání do Index metody akce, která umožňuje hledat filmy podle žánru nebo názvu.

Aktualizujte metodu Index nalezenou v Controllers/MoviesController.cs následujícím kódu:

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

Následující řádek v Index metodě akce vytvoří dotaz LINQ pro výběr filmů:

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

Dotaz je definován pouze v tomto okamžiku, nebyl spuštěn proti databázi.

searchString Pokud parametr obsahuje řetězec, dotaz filmy se upraví tak, aby filtrovaly hodnotu hledaného řetězce:

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

Výše s => s.Title!.ToUpper().Contains(searchString.ToUpper()) uvedený kód je výraz lambda. Lambda se používají v dotazech LINQ založených na metodách jako argumenty pro standardní metody operátoru dotazu, jako Where je metoda nebo Contains (používá se v kódu výše). Dotazy LINQ se nespouštějí, když jsou definovány nebo když jsou upraveny voláním metody, jako Whereje , Containsnebo OrderBy. Místo toho se odloží provádění dotazu. To znamená, že vyhodnocení výrazu je zpožděné, dokud se jeho dosažená hodnota ve skutečnosti iterated over nebo ToListAsync metoda volá. Další informace o odložené spuštění dotazu naleznete v tématu Provádění dotazů.

Poznámka:

Metoda Contains se spouští v databázi, ne v kódu jazyka C#. Citlivost případu na dotazu závisí na databázi a kolaci. Na SQL Serveru Contains se mapuje na SQL LIKE, což je nerozlišující velká a malá písmena. SQLite s výchozí kolací je kombinace rozlišování velkých a malých a velkých písmen v závislosti na dotazu. Informace o rozlišování dotazů SQLite pro případ najdete v následujících tématech:

Přejděte na /Movies/Index. Připojte řetězec dotazu, například ?searchString=Ghost k adrese URL. Zobrazí se filtrované filmy.

Zobrazení indexu

Pokud změníte podpis Index metody tak, aby měl název idparametr , id bude parametr odpovídat volitelnému {id} zástupného symbolu pro výchozí trasy nastavené v Program.cs.

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

Změňte parametr na id a změňte všechny výskyty searchString na id.

Výše uvedená metoda Index:

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

Aktualizovaná Index metoda s parametrem id :

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

Název hledání teď můžete předat jako směrovací data (segment adresy URL) místo jako hodnotu řetězce dotazu.

Zobrazení rejstříku se slovem ghost přidaným na adresu URL a vráceným seznamem filmů se dvěma filmy, Ghostbusters a Ghostbusters 2

Nemůžete ale očekávat, že uživatelé upraví adresu URL pokaždé, když chtějí hledat film. Teď tedy přidáte prvky uživatelského rozhraní, které jim pomůžou filtrovat filmy. Pokud jste změnili podpis Index metody tak, aby testoval, jak předat parametr vázaný ID na trasu, změňte ho zpět tak, aby přebírá parametr s názvem 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 Otevřete soubor a přidejte níže zvýrazněnou <form> značku:

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Značka HTML <form> používá pomocníka značky formuláře, takže při odeslání formuláře se řetězec filtru odešle do Index akce kontroleru filmů. Uložte změny a pak filtr otestujte.

Zobrazení rejstříku se slovem ghost napsaným do textového pole filtru nadpisu

Neexistuje žádné [HttpPost] přetížení Index metody, jak byste mohli očekávat. Nepotřebujete ji, protože metoda nemění stav aplikace, jenom filtrování dat.

Můžete přidat následující [HttpPost] Index metodu.

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

Parametr notUsed se používá k vytvoření přetížení pro metodu Index . O tom budeme mluvit později v tomto kurzu.

Pokud přidáte tuto metodu, vyvolání akce by odpovídalo [HttpPost] Index metodě a [HttpPost] Index metoda by se spustila, jak je znázorněno na obrázku níže.

Okno prohlížeče s odpovědí aplikace z httppost indexu: filtr na ghost

I když ale přidáte tuto [HttpPost] verzi Index metody, existuje omezení způsobu implementace. Představte si, že chcete vytvořit záložku určitého hledání nebo chcete poslat odkaz přátelům, na které můžou kliknout, aby se zobrazil stejný filtrovaný seznam filmů. Všimněte si, že adresa URL požadavku HTTP POST je stejná jako adresa URL požadavku GET (localhost:{PORT}/Movies/Index) – v adrese URL nejsou žádné informace o hledání. Informace o vyhledávacím řetězci se odešlou na server jako hodnota pole formuláře. To můžete ověřit pomocí prohlížeče Vývojářské nástroje nebo vynikajícího nástroje Fiddler. Následující obrázek ukazuje prohlížeč Chrome Vývojářské nástroje:

Síťová karta Vývojářské nástroje Microsoft Edge zobrazující text požadavku s hodnotou searchString ghost

V textu požadavku můžete vidět hledaný parametr a token XSRF . Všimněte si, jak je uvedeno v předchozím kurzu, pomocník značky formuláře generuje anti-forgery token XSRF. Neupravujeme data, takže token v metodě kontroleru nemusíme ověřovat.

Vzhledem k tomu, že parametr vyhledávání je v textu požadavku, a ne v adrese URL, nemůžete zachytit tyto informace hledání, které chcete uložit do záložek nebo sdílet s ostatními. Opravte to tak, že v souboru zadáte požadavek HTTP GET Views/Movies/Index.cshtml .

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Když teď odešlete hledání, adresa URL obsahuje řetězec vyhledávacího dotazu. Hledání také přejde na metodu HttpGet Index akce, i když máte metodu HttpPost Index .

Okno prohlížeče zobrazující řetězec searchString=ghost v adrese URL a vrácené filmy, Ghostbusters a Ghostbusters 2, obsahují slovo ghost

Následující kód ukazuje změnu značky form :

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

Přidat hledání podle žánru

Do složky Models přidejte následující MovieGenreViewModel třídu:

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 zobrazení filmového žánru bude obsahovat:

  • Seznam filmů.
  • A SelectList obsahující seznam žánrů. Uživatel tak může ze seznamu vybrat žánr.
  • MovieGenre, který obsahuje vybraný žánr.
  • SearchString, který obsahuje text, který uživatel zadá do vyhledávacího textového pole.

Nahraďte metodu Index MoviesController.cs následujícím kódem:

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

Následující kód je LINQ dotaz, který načte všechny žánry z databáze.

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

Žánry SelectList se vytvářejí tak, že promítnou různé žánry (nechceme, aby náš výběrový seznam měl duplicitní žánry).

Když uživatel vyhledá položku, hodnota hledání se zachovají do vyhledávacího pole.

Přidání vyhledávání podle žánru do zobrazení indexu

Aktualizace Index.cshtml nalezená v zobrazeních/filmy/ následujícím způsobem:

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

        Title: <input type="text" asp-for="SearchString" />
        <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>

Prozkoumejte výraz lambda použitý v následujícím pomocníkovi HTML:

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

V předchozím kódu DisplayNameFor pomocník HTML zkontroluje Title vlastnost odkazovanou ve výrazu lambda, aby určil zobrazovaný název. Vzhledem k tomu, že výraz lambda se kontroluje místo vyhodnocení, neobdržíte porušení přístupu, pokud model, model.Moviesnebo jsou nebo jsou nebo model.Movies[0] jsou null prázdné. Při vyhodnocování výrazu lambda (například @Html.DisplayFor(modelItem => item.Title)) se vyhodnocují hodnoty vlastností modelu. Následující ! je operátor null-forgiving, který se používá k deklaraci, že Movies není null.model.Movies

Otestujte aplikaci vyhledáváním podle žánru, podle názvu filmu a obojího:

Okno prohlížeče zobrazující výsledky https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

V této části přidáte funkci vyhledávání do Index metody akce, která umožňuje hledat filmy podle žánru nebo názvu.

Aktualizujte metodu Index nalezenou v Controllers/MoviesController.cs následujícím kódu:

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

Následující řádek v Index metodě akce vytvoří dotaz LINQ pro výběr filmů:

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

Dotaz je definován pouze v tomto okamžiku, nebyl spuštěn proti databázi.

searchString Pokud parametr obsahuje řetězec, dotaz filmy se upraví tak, aby filtrovaly hodnotu hledaného řetězce:

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

Výše s => s.Title!.ToUpper().Contains(searchString.ToUpper()) uvedený kód je výraz lambda. Lambda se používají v dotazech LINQ založených na metodách jako argumenty pro standardní metody operátoru dotazu, jako Where je metoda nebo Contains (používá se v kódu výše). Dotazy LINQ se nespouštějí, když jsou definovány nebo když jsou upraveny voláním metody, jako Whereje , Containsnebo OrderBy. Místo toho se odloží provádění dotazu. To znamená, že vyhodnocení výrazu je zpožděné, dokud se jeho dosažená hodnota ve skutečnosti iterated over nebo ToListAsync metoda volá. Další informace o odložené spuštění dotazu naleznete v tématu Provádění dotazů.

Poznámka:

Metoda Contains se spouští v databázi, ne v kódu jazyka C#. Citlivost případu na dotazu závisí na databázi a kolaci. Na SQL Serveru Contains se mapuje na SQL LIKE, což je nerozlišující velká a malá písmena. SQLite s výchozí kolací je kombinace rozlišování velkých a malých a velkých písmen v závislosti na dotazu. Informace o rozlišování dotazů SQLite pro případ najdete v následujících tématech:

Přejděte na /Movies/Index. Připojte řetězec dotazu, například ?searchString=Ghost k adrese URL. Zobrazí se filtrované filmy.

Zobrazení indexu

Pokud změníte podpis Index metody tak, aby měl název idparametr , id bude parametr odpovídat volitelnému {id} zástupného symbolu pro výchozí trasy nastavené v Program.cs.

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

Změňte parametr na id a změňte všechny výskyty searchString na id.

Výše uvedená metoda Index:

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

Aktualizovaná Index metoda s parametrem id :

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

Název hledání teď můžete předat jako směrovací data (segment adresy URL) místo jako hodnotu řetězce dotazu.

Zobrazení rejstříku se slovem ghost přidaným na adresu URL a vráceným seznamem filmů se dvěma filmy, Ghostbusters a Ghostbusters 2

Nemůžete ale očekávat, že uživatelé upraví adresu URL pokaždé, když chtějí hledat film. Teď tedy přidáte prvky uživatelského rozhraní, které jim pomůžou filtrovat filmy. Pokud jste změnili podpis Index metody tak, aby testoval, jak předat parametr vázaný ID na trasu, změňte ho zpět tak, aby přebírá parametr s názvem 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 Otevřete soubor a přidejte níže zvýrazněnou <form> značku:

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Značka HTML <form> používá pomocníka značky formuláře, takže při odeslání formuláře se řetězec filtru odešle do Index akce kontroleru filmů. Uložte změny a pak filtr otestujte.

Zobrazení rejstříku se slovem ghost napsaným do textového pole filtru nadpisu

Neexistuje žádné [HttpPost] přetížení Index metody, jak byste mohli očekávat. Nepotřebujete ji, protože metoda nemění stav aplikace, jenom filtrování dat.

Můžete přidat následující [HttpPost] Index metodu.

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

Parametr notUsed se používá k vytvoření přetížení pro metodu Index . O tom budeme mluvit později v tomto kurzu.

Pokud přidáte tuto metodu, vyvolání akce by odpovídalo [HttpPost] Index metodě a [HttpPost] Index metoda by se spustila, jak je znázorněno na obrázku níže.

Okno prohlížeče s odpovědí aplikace z httppost indexu: filtr na ghost

I když ale přidáte tuto [HttpPost] verzi Index metody, existuje omezení způsobu implementace. Představte si, že chcete vytvořit záložku určitého hledání nebo chcete poslat odkaz přátelům, na které můžou kliknout, aby se zobrazil stejný filtrovaný seznam filmů. Všimněte si, že adresa URL požadavku HTTP POST je stejná jako adresa URL požadavku GET (localhost:{PORT}/Movies/Index) – v adrese URL nejsou žádné informace o hledání. Informace o vyhledávacím řetězci se odešlou na server jako hodnota pole formuláře. To můžete ověřit pomocí prohlížeče Vývojářské nástroje nebo vynikajícího nástroje Fiddler. Následující obrázek ukazuje prohlížeč Chrome Vývojářské nástroje:

Síťová karta Vývojářské nástroje Microsoft Edge zobrazující text požadavku s hodnotou searchString ghost

V textu požadavku můžete vidět hledaný parametr a token XSRF . Všimněte si, jak je uvedeno v předchozím kurzu, pomocník značky formuláře generuje anti-forgery token XSRF. Neupravujeme data, takže token v metodě kontroleru nemusíme ověřovat.

Vzhledem k tomu, že parametr vyhledávání je v textu požadavku, a ne v adrese URL, nemůžete zachytit tyto informace hledání, které chcete uložit do záložek nebo sdílet s ostatními. Opravte to tak, že v souboru zadáte požadavek HTTP GET Views/Movies/Index.cshtml .

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Když teď odešlete hledání, adresa URL obsahuje řetězec vyhledávacího dotazu. Hledání také přejde na metodu HttpGet Index akce, i když máte metodu HttpPost Index .

Okno prohlížeče zobrazující řetězec searchString=ghost v adrese URL a vrácené filmy, Ghostbusters a Ghostbusters 2, obsahují slovo ghost

Následující kód ukazuje změnu značky form :

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

Přidat hledání podle žánru

Do složky Models přidejte následující MovieGenreViewModel třídu:

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 zobrazení filmového žánru bude obsahovat:

  • Seznam filmů.
  • A SelectList obsahující seznam žánrů. Uživatel tak může ze seznamu vybrat žánr.
  • MovieGenre, který obsahuje vybraný žánr.
  • SearchString, který obsahuje text, který uživatel zadá do vyhledávacího textového pole.

Nahraďte metodu Index MoviesController.cs následujícím kódem:

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

Následující kód je LINQ dotaz, který načte všechny žánry z databáze.

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

Žánry SelectList se vytvářejí tak, že promítnou různé žánry (nechceme, aby náš výběrový seznam měl duplicitní žánry).

Když uživatel vyhledá položku, hodnota hledání se zachovají do vyhledávacího pole.

Přidání vyhledávání podle žánru do zobrazení indexu

Aktualizace Index.cshtml nalezená v zobrazeních/filmy/ následujícím způsobem:

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

        Title: <input type="text" asp-for="SearchString" />
        <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>

Prozkoumejte výraz lambda použitý v následujícím pomocníkovi HTML:

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

V předchozím kódu DisplayNameFor pomocník HTML zkontroluje Title vlastnost odkazovanou ve výrazu lambda, aby určil zobrazovaný název. Vzhledem k tomu, že výraz lambda se kontroluje místo vyhodnocení, neobdržíte porušení přístupu, pokud model, model.Moviesnebo jsou nebo jsou nebo model.Movies[0] jsou null prázdné. Při vyhodnocování výrazu lambda (například @Html.DisplayFor(modelItem => item.Title)) se vyhodnocují hodnoty vlastností modelu. Následující ! je operátor null-forgiving, který se používá k deklaraci, že Movies není null.model.Movies

Otestujte aplikaci vyhledáváním podle žánru, podle názvu filmu a obojího:

Okno prohlížeče zobrazující výsledky https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

V této části přidáte funkci vyhledávání do Index metody akce, která umožňuje hledat filmy podle žánru nebo názvu.

Aktualizujte metodu Index nalezenou v Controllers/MoviesController.cs následujícím kódu:

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

Následující řádek v Index metodě akce vytvoří dotaz LINQ pro výběr filmů:

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

Dotaz je definován pouze v tomto okamžiku, nebyl spuštěn proti databázi.

searchString Pokud parametr obsahuje řetězec, dotaz filmy se upraví tak, aby filtrovaly hodnotu hledaného řetězce:

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

Výše s => s.Title!.ToUpper().Contains(searchString.ToUpper()) uvedený kód je výraz lambda. Lambda se používají v dotazech LINQ založených na metodách jako argumenty pro standardní metody operátoru dotazu, jako Where je metoda nebo Contains (používá se v kódu výše). Dotazy LINQ se nespouštějí, když jsou definovány nebo když jsou upraveny voláním metody, jako Whereje , Containsnebo OrderBy. Místo toho se odloží provádění dotazu. To znamená, že vyhodnocení výrazu je zpožděné, dokud se jeho dosažená hodnota ve skutečnosti iterated over nebo ToListAsync metoda volá. Další informace o odložené spuštění dotazu naleznete v tématu Provádění dotazů.

Poznámka:

Metoda Contains se spouští v databázi, ne v kódu jazyka C#. Citlivost případu na dotazu závisí na databázi a kolaci. Na SQL Serveru Contains se mapuje na SQL LIKE, což je nerozlišující velká a malá písmena. SQLite s výchozí kolací je kombinace rozlišování velkých a malých a velkých písmen v závislosti na dotazu. Informace o rozlišování dotazů SQLite pro případ najdete v následujících tématech:

Přejděte na /Movies/Index. Připojte řetězec dotazu, například ?searchString=Ghost k adrese URL. Zobrazí se filtrované filmy.

Zobrazení indexu

Pokud změníte podpis Index metody tak, aby měl název idparametr , id bude parametr odpovídat volitelnému {id} zástupného symbolu pro výchozí trasy nastavené v Program.cs.

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

Změňte parametr na id a změňte všechny výskyty searchString na id.

Výše uvedená metoda Index:

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

Aktualizovaná Index metoda s parametrem id :

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

Název hledání teď můžete předat jako směrovací data (segment adresy URL) místo jako hodnotu řetězce dotazu.

Zobrazení rejstříku se slovem ghost přidaným na adresu URL a vráceným seznamem filmů se dvěma filmy, Ghostbusters a Ghostbusters 2

Nemůžete ale očekávat, že uživatelé upraví adresu URL pokaždé, když chtějí hledat film. Teď tedy přidáte prvky uživatelského rozhraní, které jim pomůžou filtrovat filmy. Pokud jste změnili podpis Index metody tak, aby testoval, jak předat parametr vázaný ID na trasu, změňte ho zpět tak, aby přebírá parametr s názvem 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 Otevřete soubor a přidejte níže zvýrazněnou <form> značku:

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Značka HTML <form> používá pomocníka značky formuláře, takže při odeslání formuláře se řetězec filtru odešle do Index akce kontroleru filmů. Uložte změny a pak filtr otestujte.

Zobrazení rejstříku se slovem ghost napsaným do textového pole filtru nadpisu

Neexistuje žádné [HttpPost] přetížení Index metody, jak byste mohli očekávat. Nepotřebujete ji, protože metoda nemění stav aplikace, jenom filtrování dat.

Můžete přidat následující [HttpPost] Index metodu.

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

Parametr notUsed se používá k vytvoření přetížení pro metodu Index . O tom budeme mluvit později v tomto kurzu.

Pokud přidáte tuto metodu, vyvolání akce by odpovídalo [HttpPost] Index metodě a [HttpPost] Index metoda by se spustila, jak je znázorněno na obrázku níže.

Okno prohlížeče s odpovědí aplikace z httppost indexu: filtr na ghost

I když ale přidáte tuto [HttpPost] verzi Index metody, existuje omezení způsobu implementace. Představte si, že chcete vytvořit záložku určitého hledání nebo chcete poslat odkaz přátelům, na které můžou kliknout, aby se zobrazil stejný filtrovaný seznam filmů. Všimněte si, že adresa URL požadavku HTTP POST je stejná jako adresa URL požadavku GET (localhost:{PORT}/Movies/Index) – v adrese URL nejsou žádné informace o hledání. Informace o vyhledávacím řetězci se odešlou na server jako hodnota pole formuláře. To můžete ověřit pomocí prohlížeče Vývojářské nástroje nebo vynikajícího nástroje Fiddler. Následující obrázek ukazuje prohlížeč Chrome Vývojářské nástroje:

Síťová karta Vývojářské nástroje Microsoft Edge zobrazující text požadavku s hodnotou searchString ghost

V textu požadavku můžete vidět hledaný parametr a token XSRF . Všimněte si, jak je uvedeno v předchozím kurzu, pomocník značky formuláře generuje anti-forgery token XSRF. Neupravujeme data, takže token v metodě kontroleru nemusíme ověřovat.

Vzhledem k tomu, že parametr vyhledávání je v textu požadavku, a ne v adrese URL, nemůžete zachytit tyto informace hledání, které chcete uložit do záložek nebo sdílet s ostatními. Opravte to tak, že v souboru zadáte požadavek HTTP GET Views/Movies/Index.cshtml .

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Když teď odešlete hledání, adresa URL obsahuje řetězec vyhledávacího dotazu. Hledání také přejde na metodu HttpGet Index akce, i když máte metodu HttpPost Index .

Okno prohlížeče zobrazující řetězec searchString=ghost v adrese URL a vrácené filmy, Ghostbusters a Ghostbusters 2, obsahují slovo ghost

Následující kód ukazuje změnu značky form :

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

Přidat hledání podle žánru

Do složky Models přidejte následující MovieGenreViewModel třídu:

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 zobrazení filmového žánru bude obsahovat:

  • Seznam filmů.
  • A SelectList obsahující seznam žánrů. Uživatel tak může ze seznamu vybrat žánr.
  • MovieGenre, který obsahuje vybraný žánr.
  • SearchString, který obsahuje text, který uživatel zadá do vyhledávacího textového pole.

Nahraďte metodu Index MoviesController.cs následujícím kódem:

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

Následující kód je LINQ dotaz, který načte všechny žánry z databáze.

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

Žánry SelectList se vytvářejí tak, že promítnou různé žánry (nechceme, aby náš výběrový seznam měl duplicitní žánry).

Když uživatel vyhledá položku, hodnota hledání se zachovají do vyhledávacího pole.

Přidání vyhledávání podle žánru do zobrazení indexu

Aktualizace Index.cshtml nalezená v zobrazeních/filmy/ následujícím způsobem:

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

        Title: <input type="text" asp-for="SearchString" />
        <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>

Prozkoumejte výraz lambda použitý v následujícím pomocníkovi HTML:

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

V předchozím kódu DisplayNameFor pomocník HTML zkontroluje Title vlastnost odkazovanou ve výrazu lambda, aby určil zobrazovaný název. Vzhledem k tomu, že výraz lambda se kontroluje místo vyhodnocení, neobdržíte porušení přístupu, pokud model, model.Moviesnebo jsou nebo jsou nebo model.Movies[0] jsou null prázdné. Při vyhodnocování výrazu lambda (například @Html.DisplayFor(modelItem => item.Title)) se vyhodnocují hodnoty vlastností modelu. Následující ! je operátor null-forgiving, který se používá k deklaraci, že Movies není null.model.Movies

Otestujte aplikaci vyhledáváním podle žánru, podle názvu filmu a obojího:

Okno prohlížeče zobrazující výsledky https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

V této části přidáte funkci vyhledávání do Index metody akce, která umožňuje hledat filmy podle žánru nebo názvu.

Aktualizujte metodu Index nalezenou v Controllers/MoviesController.cs následujícím kódu:

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

První řádek Index metody akce vytvoří dotaz LINQ pro výběr filmů:

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

Dotaz je definován pouze v tomto okamžiku, nebyl spuštěn proti databázi.

searchString Pokud parametr obsahuje řetězec, dotaz filmy se upraví tak, aby filtrovaly hodnotu hledaného řetězce:

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

Výše s => s.Title!.Contains(searchString) uvedený kód je výraz lambda. Lambda se používají v dotazech LINQ založených na metodách jako argumenty pro standardní metody operátoru dotazu, jako Where je metoda nebo Contains (používá se v kódu výše). Dotazy LINQ se nespouštějí, když jsou definovány nebo když jsou upraveny voláním metody, jako Whereje , Containsnebo OrderBy. Místo toho se odloží provádění dotazu. To znamená, že vyhodnocení výrazu je zpožděné, dokud se jeho dosažená hodnota ve skutečnosti iterated over nebo ToListAsync metoda volá. Další informace o odložené spuštění dotazu naleznete v tématu Provádění dotazů.

Poznámka: Metoda Contains se spouští v databázi, ne v kódu c#uvedeném výše. Citlivost případu na dotazu závisí na databázi a kolaci. Na SQL Serveru Contains se mapuje na SQL LIKE, což je nerozlišující velká a malá písmena. U SQLite s výchozí kolací se rozlišují malá a velká písmena.

Přejděte na /Movies/Index. Připojte řetězec dotazu, například ?searchString=Ghost k adrese URL. Zobrazí se filtrované filmy.

Zobrazení indexu

Pokud změníte podpis Index metody tak, aby měl název idparametr , id bude parametr odpovídat volitelnému {id} zástupného symbolu pro výchozí trasy nastavené v Program.cs.

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

Změňte parametr na id a změňte všechny výskyty searchString na id.

Výše uvedená metoda Index:

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

Aktualizovaná Index metoda s parametrem id :

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

Název hledání teď můžete předat jako směrovací data (segment adresy URL) místo jako hodnotu řetězce dotazu.

Zobrazení rejstříku se slovem ghost přidaným na adresu URL a vráceným seznamem filmů se dvěma filmy, Ghostbusters a Ghostbusters 2

Nemůžete ale očekávat, že uživatelé upraví adresu URL pokaždé, když chtějí hledat film. Teď tedy přidáte prvky uživatelského rozhraní, které jim pomůžou filtrovat filmy. Pokud jste změnili podpis Index metody tak, aby testoval, jak předat parametr vázaný ID na trasu, změňte ho zpět tak, aby přebírá parametr s názvem 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 Otevřete soubor a přidejte níže zvýrazněnou <form> značku:

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>

Značka HTML <form> používá pomocníka značky formuláře, takže při odeslání formuláře se řetězec filtru odešle do Index akce kontroleru filmů. Uložte změny a pak filtr otestujte.

Zobrazení rejstříku se slovem ghost napsaným do textového pole filtru nadpisu

Neexistuje žádné [HttpPost] přetížení Index metody, jak byste mohli očekávat. Nepotřebujete ji, protože metoda nemění stav aplikace, jenom filtrování dat.

Můžete přidat následující [HttpPost] Index metodu.

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

Parametr notUsed se používá k vytvoření přetížení pro metodu Index . O tom budeme mluvit později v tomto kurzu.

Pokud přidáte tuto metodu, vyvolání akce by odpovídalo [HttpPost] Index metodě a [HttpPost] Index metoda by se spustila, jak je znázorněno na obrázku níže.

Okno prohlížeče s odpovědí aplikace z httppost indexu: filtr na ghost

I když ale přidáte tuto [HttpPost] verzi Index metody, existuje omezení způsobu implementace. Představte si, že chcete vytvořit záložku určitého hledání nebo chcete poslat odkaz přátelům, na které můžou kliknout, aby se zobrazil stejný filtrovaný seznam filmů. Všimněte si, že adresa URL požadavku HTTP POST je stejná jako adresa URL požadavku GET (localhost:{PORT}/Movies/Index) – v adrese URL nejsou žádné informace o hledání. Informace o vyhledávacím řetězci se odešlou na server jako hodnota pole formuláře. To můžete ověřit pomocí prohlížeče Vývojářské nástroje nebo vynikajícího nástroje Fiddler. Následující obrázek ukazuje prohlížeč Chrome Vývojářské nástroje:

Síťová karta Vývojářské nástroje Microsoft Edge zobrazující text požadavku s hodnotou searchString ghost

V textu požadavku můžete vidět hledaný parametr a token XSRF . Všimněte si, jak je uvedeno v předchozím kurzu, pomocník značky formuláře generuje anti-forgery token XSRF. Neupravujeme data, takže token v metodě kontroleru nemusíme ověřovat.

Vzhledem k tomu, že parametr vyhledávání je v textu požadavku, a ne v adrese URL, nemůžete zachytit tyto informace hledání, které chcete uložit do záložek nebo sdílet s ostatními. Opravte to tak, že v souboru zadáte požadavek HTTP GET Views/Movies/Index.cshtml .

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>
<table class="table">

Když teď odešlete hledání, adresa URL obsahuje řetězec vyhledávacího dotazu. Hledání také přejde na metodu HttpGet Index akce, i když máte metodu HttpPost Index .

Okno prohlížeče zobrazující řetězec searchString=ghost v adrese URL a vrácené filmy, Ghostbusters a Ghostbusters 2, obsahují slovo ghost

Následující kód ukazuje změnu značky form :

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

Přidat hledání podle žánru

Do složky Models přidejte následující MovieGenreViewModel třídu:

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 zobrazení filmového žánru bude obsahovat:

  • Seznam filmů.
  • A SelectList obsahující seznam žánrů. Uživatel tak může ze seznamu vybrat žánr.
  • MovieGenre, který obsahuje vybraný žánr.
  • SearchString, který obsahuje text, který uživatel zadá do vyhledávacího textového pole.

Nahraďte metodu Index MoviesController.cs následujícím kódem:

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

Následující kód je LINQ dotaz, který načte všechny žánry z databáze.

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

Žánry SelectList se vytvářejí tak, že promítnou různé žánry (nechceme, aby náš výběrový seznam měl duplicitní žánry).

Když uživatel vyhledá položku, hodnota hledání se zachovají do vyhledávacího pole.

Přidání vyhledávání podle žánru do zobrazení indexu

Aktualizace Index.cshtml nalezená v zobrazeních/filmy/ následujícím způsobem:

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

        Title: <input type="text" asp-for="SearchString" />
        <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>

Prozkoumejte výraz lambda použitý v následujícím pomocníkovi HTML:

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

V předchozím kódu DisplayNameFor pomocník HTML zkontroluje Title vlastnost odkazovanou ve výrazu lambda, aby určil zobrazovaný název. Vzhledem k tomu, že výraz lambda se kontroluje místo vyhodnocení, neobdržíte porušení přístupu, pokud model, model.Moviesnebo jsou nebo jsou nebo model.Movies[0] jsou null prázdné. Při vyhodnocování výrazu lambda (například @Html.DisplayFor(modelItem => item.Title)) se vyhodnocují hodnoty vlastností modelu.

Otestujte aplikaci vyhledáváním podle žánru, podle názvu filmu a obojího:

Okno prohlížeče zobrazující výsledky https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2

V této části přidáte funkci vyhledávání do Index metody akce, která umožňuje hledat filmy podle žánru nebo názvu.

Aktualizujte metodu Index nalezenou v Controllers/MoviesController.cs následujícím kódu:

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

První řádek Index metody akce vytvoří dotaz LINQ pro výběr filmů:

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

Dotaz je definován pouze v tomto okamžiku, nebyl spuštěn proti databázi.

searchString Pokud parametr obsahuje řetězec, dotaz filmy se upraví tak, aby filtrovaly hodnotu hledaného řetězce:

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

Výše s => s.Title.Contains() uvedený kód je výraz lambda. Lambda se používají v dotazech LINQ založených na metodách jako argumenty pro standardní metody operátoru dotazu, jako Where je metoda nebo Contains (používá se v kódu výše). Dotazy LINQ se nespouštějí, když jsou definovány nebo když jsou upraveny voláním metody, jako Whereje , Containsnebo OrderBy. Místo toho se odloží provádění dotazu. To znamená, že vyhodnocení výrazu je zpožděné, dokud se jeho dosažená hodnota ve skutečnosti iterated over nebo ToListAsync metoda volá. Další informace o odložené spuštění dotazu naleznete v tématu Provádění dotazů.

Poznámka: Metoda Contains se spouští v databázi, ne v kódu c#uvedeném výše. Citlivost případu na dotazu závisí na databázi a kolaci. Na SQL Serveru Contains se mapuje na SQL LIKE, což je nerozlišující velká a malá písmena. U SQLite s výchozí kolací se rozlišují malá a velká písmena.

Přejděte na /Movies/Index. Připojte řetězec dotazu, například ?searchString=Ghost k adrese URL. Zobrazí se filtrované filmy.

Zobrazení indexu

Pokud změníte podpis Index metody tak, aby měl název idparametr , id bude parametr odpovídat volitelnému {id} zástupného symbolu pro výchozí trasy nastavené v Startup.cs.

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

Změňte parametr na id a všechny výskyty searchString změn na id.

Výše uvedená metoda Index:

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

Aktualizovaná Index metoda s parametrem id :

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

Název hledání teď můžete předat jako směrovací data (segment adresy URL) místo jako hodnotu řetězce dotazu.

Zobrazení rejstříku se slovem ghost přidaným na adresu URL a vráceným seznamem filmů se dvěma filmy, Ghostbusters a Ghostbusters 2

Nemůžete ale očekávat, že uživatelé upraví adresu URL pokaždé, když chtějí hledat film. Teď tedy přidáte prvky uživatelského rozhraní, které jim pomůžou filtrovat filmy. Pokud jste změnili podpis Index metody tak, aby testoval, jak předat parametr vázaný ID na trasu, změňte ho zpět tak, aby přebírá parametr s názvem 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 Otevřete soubor a přidejte níže zvýrazněnou <form> značku:

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

<h2>Index</h2>

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

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

<table class="table">
    <thead>

Značka HTML <form> používá pomocníka značky formuláře, takže při odeslání formuláře se řetězec filtru odešle do Index akce kontroleru filmů. Uložte změny a pak filtr otestujte.

Zobrazení rejstříku se slovem ghost napsaným do textového pole filtru nadpisu

Neexistuje žádné [HttpPost] přetížení Index metody, jak byste mohli očekávat. Nepotřebujete ji, protože metoda nemění stav aplikace, jenom filtrování dat.

Můžete přidat následující [HttpPost] Index metodu.

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

Parametr notUsed se používá k vytvoření přetížení pro metodu Index . O tom budeme mluvit později v tomto kurzu.

Pokud přidáte tuto metodu, vyvolání akce by odpovídalo [HttpPost] Index metodě a [HttpPost] Index metoda by se spustila, jak je znázorněno na obrázku níže.

Okno prohlížeče s odpovědí aplikace z httppost indexu: filtr na ghost

I když ale přidáte tuto [HttpPost] verzi Index metody, existuje omezení způsobu implementace. Představte si, že chcete vytvořit záložku určitého hledání nebo chcete poslat odkaz přátelům, na které můžou kliknout, aby se zobrazil stejný filtrovaný seznam filmů. Všimněte si, že adresa URL požadavku HTTP POST je stejná jako adresa URL požadavku GET (localhost:{PORT}/Movies/Index) – v adrese URL nejsou žádné informace o hledání. Informace o vyhledávacím řetězci se odešlou na server jako hodnota pole formuláře. To můžete ověřit pomocí prohlížeče Vývojářské nástroje nebo vynikajícího nástroje Fiddler. Následující obrázek ukazuje prohlížeč Chrome Vývojářské nástroje:

Síťová karta Vývojářské nástroje Microsoft Edge zobrazující text požadavku s hodnotou searchString ghost

V textu požadavku můžete vidět hledaný parametr a token XSRF . Všimněte si, jak je uvedeno v předchozím kurzu, pomocník značky formuláře generuje anti-forgery token XSRF. Neupravujeme data, takže token v metodě kontroleru nemusíme ověřovat.

Vzhledem k tomu, že parametr vyhledávání je v textu požadavku, a ne v adrese URL, nemůžete zachytit tyto informace hledání, které chcete uložit do záložek nebo sdílet s ostatními. Opravte to tak, že v souboru zadáte požadavek HTTP GET Views/Movies/Index.cshtml .

@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>
        Title: <input type="text" name="SearchString" />
        <input type="submit" value="Filter" />
    </p>
</form>

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

Když teď odešlete hledání, adresa URL obsahuje řetězec vyhledávacího dotazu. Hledání také přejde na metodu HttpGet Index akce, i když máte metodu HttpPost Index .

Okno prohlížeče zobrazující řetězec searchString=ghost v adrese URL a vrácené filmy, Ghostbusters a Ghostbusters 2, obsahují slovo ghost

Následující kód ukazuje změnu značky form :

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

Přidat hledání podle žánru

Do složky Models přidejte následující MovieGenreViewModel třídu:

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 zobrazení filmového žánru bude obsahovat:

  • Seznam filmů.
  • A SelectList obsahující seznam žánrů. Uživatel tak může ze seznamu vybrat žánr.
  • MovieGenre, který obsahuje vybraný žánr.
  • SearchString, který obsahuje text, který uživatel zadá do vyhledávacího textového pole.

Nahraďte metodu Index MoviesController.cs následujícím kódem:

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

Následující kód je LINQ dotaz, který načte všechny žánry z databáze.

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

Žánry SelectList se vytvářejí tak, že promítnou různé žánry (nechceme, aby náš výběrový seznam měl duplicitní žánry).

Když uživatel vyhledá položku, hodnota hledání se zachovají do vyhledávacího pole.

Přidání vyhledávání podle žánru do zobrazení indexu

Aktualizace Index.cshtml nalezená v zobrazeních/filmy/ následujícím způsobem:

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

        Title: <input type="text" asp-for="SearchString" />
        <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>

Prozkoumejte výraz lambda použitý v následujícím pomocníkovi HTML:

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

V předchozím kódu DisplayNameFor pomocník HTML zkontroluje Title vlastnost odkazovanou ve výrazu lambda, aby určil zobrazovaný název. Vzhledem k tomu, že výraz lambda se kontroluje místo vyhodnocení, neobdržíte porušení přístupu, pokud model, model.Moviesnebo jsou nebo jsou nebo model.Movies[0] jsou null prázdné. Při vyhodnocování výrazu lambda (například @Html.DisplayFor(modelItem => item.Title)) se vyhodnocují hodnoty vlastností modelu.

Otestujte aplikaci vyhledáváním podle žánru, podle názvu filmu a obojího:

Okno prohlížeče zobrazující výsledky https://localhost:5001/Movies?MovieGenre=Comedy& SearchString=2