Čá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 Where
je , Contains
nebo 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.
Pokud změníte podpis Index
metody tak, aby měl název id
parametr , 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.
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>
<label>Title: <input type="text" name="SearchString" /></label>
<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.
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.
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 vybranými kartami Síť a Záhlaví:
Karty Síť a datová část jsou vybrány pro zobrazení dat formuláře:
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 vygeneruje antiforgery 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 zadáním požadavku HTTP GET
na form
značku, která se nachází v Views/Movies/Index.cshtml
souboru.
@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">
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
.
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>
<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>
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.Movies
nebo 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:
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 Where
je , Contains
nebo 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.
Pokud změníte podpis Index
metody tak, aby měl název id
parametr , 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.
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>
<label>Title: <input type="text" name="SearchString" /></label>
<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.
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.
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:
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 vygeneruje antiforgery 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>
<label>Title: <input type="text" name="SearchString" /></label>
<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
.
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>
<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>
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.Movies
nebo 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:
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 Where
je , Contains
nebo 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.
Pokud změníte podpis Index
metody tak, aby měl název id
parametr , 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.
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>
<label>Title: <input type="text" name="SearchString" /></label>
<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.
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.
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:
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 vygeneruje antiforgery 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>
<label>Title: <input type="text" name="SearchString" /></label>
<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
.
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>
<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>
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.Movies
nebo 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:
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 Where
je , Contains
nebo 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.
Pokud změníte podpis Index
metody tak, aby měl název id
parametr , 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.
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>
<label>Title: <input type="text" name="SearchString" /></label>
<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.
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.
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:
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 vygeneruje antiforgery 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>
<label>Title: <input type="text" name="SearchString" /></label>
<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
.
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>
<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>
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.Movies
nebo 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:
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 Where
je , Contains
nebo 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.
Pokud změníte podpis Index
metody tak, aby měl název id
parametr , 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.
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>
<label>Title: <input type="text" name="SearchString" /></label>
<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.
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.
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:
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 vygeneruje antiforgery 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>
<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)
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
.
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>
<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>
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.Movies
nebo 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: