Deel 7: zoekactie toevoegen aan een ASP.NET Core MVC-app
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
Door Rick Anderson
In deze sectie voegt u de zoekfunctie toe aan de actiemethode Index
waarmee u films kunt doorzoeken op genre of naam.
Werk de Index
methode in Controllers/MoviesController.cs
bij met de volgende code:
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());
}
Met de volgende regel in de actiemethode Index
wordt een LINQ--query gemaakt om de films te selecteren:
var movies = from m in _context.Movie
select m;
De query is op dit moment alleen gedefinieerd, deze is niet uitgevoerd op de database.
Als de parameter searchString
een tekenreeks bevat, wordt de filmquery gewijzigd om te filteren op de waarde van de zoekreeks:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
De s => s.Title!.ToUpper().Contains(searchString.ToUpper())
code hierboven is een lambda-expressie. Lambdas worden gebruikt in op methoden gebaseerde LINQ--query's als argumenten voor standaardqueryoperatormethoden, zoals de methode Where of Contains
(gebruikt in de bovenstaande code). LINQ-query's worden niet uitgevoerd wanneer ze zijn gedefinieerd of wanneer ze worden gewijzigd door een methode aan te roepen, zoals Where
, Contains
of OrderBy
. In plaats daarvan wordt de uitvoering van queries uitgesteld. Dat betekent dat de evaluatie van een expressie wordt vertraagd totdat de gerealiseerde waarde daadwerkelijk wordt herhaald of de methode ToListAsync
wordt aangeroepen. Zie queryuitvoeringvoor meer informatie over de uitvoering van uitgestelde query's.
Notitie
De methode Contains wordt uitgevoerd in de database, niet in de C#-code. De hoofdlettergevoeligheid voor de query is afhankelijk van de database en de sortering. Op SQL Server wordt Contains
gemapt naar SQL LIKE, wat niet hoofdlettergevoelig is. SQLite met de standaardsortering is een combinatie van hoofdlettergevoeligheid en andere gevoeligheden IN, afhankelijk van de query. Zie het volgende voor informatie over het maken van niet-hoofdlettergevoelige SQLite-query's:
Ga naar /Movies/Index
. Voeg een querytekenreeks zoals ?searchString=Ghost
toe aan de URL. De gefilterde films worden weergegeven.
Als u de handtekening van de methode Index
wijzigt om een parameter met de naam id
te hebben, komt de id
parameter overeen met de optionele {id}
tijdelijke aanduiding voor de standaardroutes die zijn ingesteld in Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Wijzig de parameter in id
en wijzig alle voorkomens van searchString
in id
.
De vorige Index
-methode:
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());
}
De bijgewerkte methode Index
met id
parameter:
public async Task<IActionResult> Index(string id)
{
if (_context.Movie == null)
{
return Problem("Entity set 'MvcMovieContext.Movie' is null.");
}
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(id))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
}
return View(await movies.ToListAsync());
}
U kunt nu de zoektitel doorgeven als routegegevens (een URL-segment) in plaats van als een querytekenreekswaarde.
U kunt echter niet verwachten dat gebruikers de URL telkens wijzigen wanneer ze naar een film willen zoeken. Nu voegt u ui-elementen toe om films te filteren. Als u de handtekening van de methode Index
hebt gewijzigd om te testen hoe u de routegebonden ID
parameter doorgeeft, wijzigt u deze weer zodat er een parameter met de naam searchString
wordt gebruikt:
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());
}
Open het bestand Views/Movies/Index.cshtml
en voeg de <form>
markeringen toe die hieronder zijn gemarkeerd:
@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">
De HTML-<form>
-tag maakt gebruik van de Helper voor formuliertags, dus wanneer u het formulier verzendt, wordt de filtertekenreeks geplaatst op de Index
actie van de filmcontroller. Sla uw wijzigingen op en test het filter.
Er is geen [HttpPost]
overbelasting van de Index
methode zoals u zou verwachten. U hebt deze niet nodig, omdat de methode de status van de app niet wijzigt, maar alleen gegevens filtert.
U kunt de volgende [HttpPost] Index
methode toevoegen.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
De parameter notUsed
wordt gebruikt om een overbelasting te maken voor de Index
methode. Verderop in de zelfstudie bespreken we dat.
Als u deze methode toevoegt, komt de actie-aanroeper overeen met de [HttpPost] Index
methode en wordt de methode [HttpPost] Index
uitgevoerd zoals wordt weergegeven in de onderstaande afbeelding.
Zelfs als u deze [HttpPost]
versie van de methode Index
toevoegt, is er een beperking in hoe dit allemaal is geïmplementeerd. Stel dat u een bladwijzer wilt maken voor een bepaalde zoekopdracht of dat u een koppeling naar vrienden wilt verzenden waarop ze kunnen klikken om dezelfde gefilterde lijst met films te zien. U ziet dat de URL voor de HTTP POST-aanvraag hetzelfde is als de URL voor de GET-aanvraag (localhost:{PORT}/Movies/Index): er is geen zoekinformatie in de URL. De gegevens van de zoekreeks worden naar de server verzonden als een formulierveldwaarde. U kunt dat verifiëren met de ontwikkelaarshulpmiddelen van de browser of het uitstekende Fiddler-hulpmiddel.
In de volgende afbeelding ziet u de ontwikkelhulpprogramma's van de Chrome-browser met de tabbladen Network en Headers geselecteerd:
De tabbladen Network en Payload zijn geselecteerd om formuliergegevens weer te geven:
U ziet de zoekparameter en XSRF--token in de aanvraagtekst. Zoals vermeld in de vorige tutorial, genereert de Form Tag Helper een XSRF- anti-forgery-token. We wijzigen geen gegevens, dus we hoeven het token niet te valideren in de controllermethode.
Omdat de zoekparameter zich in de aanvraagtekst bevindt en niet de URL, kunt u die zoekgegevens niet vastleggen om een bladwijzer te maken of met anderen te delen. Los dit op door te specificeren dat de aanvraag HTTP GET
moet zijn in de form
-tag die in het Views/Movies/Index.cshtml
-bestand gevonden wordt.
@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">
Wanneer u nu een zoekopdracht verzendt, bevat de URL de zoekquerytekenreeks. Zoeken gaat ook naar de actiemethode HttpGet Index
, zelfs als u een HttpPost Index
methode hebt.
Zoeken op genre toevoegen
Voeg de volgende MovieGenreViewModel
-klasse toe aan de map Models:
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; }
}
Het weergavemodel voor filmgenres bevat:
- Een lijst met films.
- Een
SelectList
die de lijst met genres bevat. Hierdoor kan de gebruiker een genre selecteren in de lijst. -
MovieGenre
, dat het geselecteerde genre bevat. -
SearchString
, die de tekst bevat die gebruikers invoeren in het zoekvak.
Vervang de methode Index
in MoviesController.cs
door de volgende code:
// 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);
}
De volgende code is een LINQ
query waarmee alle genres uit de database worden opgehaald.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
De SelectList
van genres wordt gemaakt door de verschillende genres te projecteren (we willen niet dat onze selectielijst dubbele genres heeft).
Wanneer de gebruiker naar het item zoekt, blijft de zoekwaarde behouden in het zoekvak.
Zoeken op genre toevoegen aan de indexweergave
Werk Index.cshtml
als volgt bij in Views/Movies/:
@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>
Bekijk de lambda-expressie die wordt gebruikt in de volgende HTML-helper:
@Html.DisplayNameFor(model => model.Movies![0].Title)
In de voorgaande code inspecteert de DisplayNameFor
HTML Helper de eigenschap Title
waarnaar wordt verwezen in de lambda-expressie om de weergavenaam te bepalen. Omdat de lambda-expressie wordt geïnspecteerd in plaats van geëvalueerd, ontvangt u geen toegangsfout wanneer model
, model.Movies
of model.Movies[0]
null
of leeg zijn. Wanneer de lambda-expressie wordt geëvalueerd (bijvoorbeeld @Html.DisplayFor(modelItem => item.Title)
), worden de eigenschapswaarden van het model geëvalueerd. De !
na model.Movies
is de operator null-forgiving-operator, die wordt gebruikt om aan te geven dat Movies
niet null is.
Test de app door te zoeken op genre, op filmtitel en op beide:
In deze sectie voegt u de zoekfunctie toe aan de actiemethode Index
waarmee u films kunt doorzoeken op genre of naam.
Werk de Index
methode in Controllers/MoviesController.cs
bij met de volgende code:
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());
}
Met de volgende regel in de actiemethode Index
wordt een LINQ--query gemaakt om de films te selecteren:
var movies = from m in _context.Movie
select m;
De query is op dit moment alleen gedefinieerd, deze is niet uitgevoerd op de database.
Als de parameter searchString
een tekenreeks bevat, wordt de filmquery gewijzigd om te filteren op de waarde van de zoekreeks:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
De hierboven genoemde s => s.Title!.ToUpper().Contains(searchString.ToUpper())
code is een Lambda-expressie. Lambdas worden gebruikt in op methoden gebaseerde LINQ--query's als argumenten voor standaardqueryoperatormethoden, zoals de methode Where of Contains
(gebruikt in de bovenstaande code). LINQ-query's worden niet uitgevoerd wanneer ze zijn gedefinieerd of wanneer ze worden gewijzigd door een methode aan te roepen, zoals Where
, Contains
of OrderBy
. In plaats daarvan wordt de uitvoering van query's uitgesteld. Dat betekent dat de evaluatie van een uitdrukking wordt vertraagd totdat de gerealiseerde waarde daadwerkelijk wordt geïtereerd over of de methode ToListAsync
wordt aangeroepen. Zie queryuitvoeringvoor meer informatie over de uitvoering van uitgestelde query's.
Notitie
De methode Contains wordt uitgevoerd in de database, niet in de C#-code. De hoofdlettergevoeligheid voor de query is afhankelijk van de database en de sortering. Op SQL Server komt Contains
overeen met SQL LIKE, wat hoofdlettergevoeligheid negeert. SQLite met de standaardsortering is een combinatie van hoofdlettergevoelig en hoofdletterongevoelig IN, afhankelijk van de query. Zie het volgende voor informatie over het maken van niet-hoofdlettergevoelige SQLite-query's:
Ga naar /Movies/Index
. Voeg een querytekenreeks zoals ?searchString=Ghost
toe aan de URL. De gefilterde films worden weergegeven.
Als u de handtekening van de methode Index
wijzigt om een parameter met de naam id
te hebben, komt de id
parameter overeen met de optionele {id}
tijdelijke aanduiding voor de standaardroutes die zijn ingesteld in Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Wijzig de parameter naar id
en wijzig alle voorkomens van searchString
naar id
.
De vorige Index
methode:
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());
}
De bijgewerkte methode Index
met id
parameter:
public async Task<IActionResult> Index(string id)
{
if (_context.Movie == null)
{
return Problem("Entity set 'MvcMovieContext.Movie' is null.");
}
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(id))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
}
return View(await movies.ToListAsync());
}
U kunt nu de zoektitel doorgeven als routegegevens (een URL-segment) in plaats van als een querytekenreekswaarde.
U kunt echter niet verwachten dat gebruikers de URL telkens wijzigen wanneer ze naar een film willen zoeken. Nu voegt u ui-elementen toe om films te filteren. Als u de handtekening van de methode Index
hebt gewijzigd om te testen hoe u de routegebonden ID
parameter doorgeeft, wijzigt u deze weer zodat er een parameter met de naam searchString
wordt gebruikt:
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());
}
Open het bestand Views/Movies/Index.cshtml
en voeg de <form>
markeringen toe die hieronder zijn gemarkeerd:
@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">
De HTML-<form>
-tag gebruikt de Form Tag Helper, dus wanneer u het formulier verzendt, wordt de filtertekenreeks gepost naar de Index
-actie van de movies-controller. Sla uw wijzigingen op en test het filter.
Er is geen [HttpPost]
overbelasting van de Index
methode zoals u zou verwachten. U hebt deze niet nodig, omdat de methode de status van de app niet wijzigt, maar alleen gegevens filtert.
U kunt de volgende [HttpPost] Index
methode toevoegen.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
De parameter notUsed
wordt gebruikt om een overbelasting te maken voor de Index
methode. Verderop in de zelfstudie bespreken we dat.
Als u deze methode toevoegt, komt de actie-aanroeper overeen met de [HttpPost] Index
methode en wordt de methode [HttpPost] Index
uitgevoerd zoals wordt weergegeven in de onderstaande afbeelding.
Zelfs als u deze [HttpPost]
versie van de methode Index
toevoegt, is er een beperking in hoe dit allemaal is geïmplementeerd. Stel dat u een bladwijzer wilt maken voor een bepaalde zoekopdracht of dat u een koppeling naar vrienden wilt verzenden waarop ze kunnen klikken om dezelfde gefilterde lijst met films te zien. U ziet dat de URL voor de HTTP POST-aanvraag hetzelfde is als de URL voor de GET-aanvraag (localhost:{PORT}/Movies/Index): er is geen zoekinformatie in de URL. De gegevens van de zoekreeks worden naar de server verzonden als een formulierveldwaarde. U kunt dat verifiëren met de ontwikkelaarshulpprogramma's van de browser of het uitstekende Fiddler-hulpprogramma. In de onderstaande afbeelding ziet u de ontwikkelhulpprogramma's van de Chrome-browser:
U ziet de zoekparameter en XSRF- token in de aanvraagbody. Zoals in de vorige zelfstudie is vermeld, genereert de Form Tag Helper een antiforgery-token voor XSRF. We wijzigen geen gegevens, dus we hoeven het token niet te valideren in de controllermethode.
Omdat de zoekparameter zich in de aanvraagtekst bevindt en niet de URL, kunt u die zoekgegevens niet vastleggen om een bladwijzer te maken of met anderen te delen. Los dit op door op te geven dat de aanvraag zich moet bevinden in het Views/Movies/Index.cshtml
-bestand op locatie HTTP GET
.
@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">
Wanneer u nu een zoekopdracht verzendt, bevat de URL de zoekquerytekenreeks. Zoeken gaat ook naar de actiemethode HttpGet Index
, zelfs als u een HttpPost Index
methode hebt.
In de volgende markeringen ziet u de wijziging in de tag form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Zoeken op genre toevoegen
Voeg de volgende MovieGenreViewModel
-klasse toe aan de map Models:
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; }
}
Het weergavemodel filmgenre zal bevatten:
- Een lijst met films.
- Een
SelectList
bevat de lijst met genres. Hierdoor kan de gebruiker een genre selecteren in de lijst. -
MovieGenre
, dat het geselecteerde genre bevat. -
SearchString
, die de tekst bevat die gebruikers invoeren in het zoekvak.
Vervang de methode Index
in MoviesController.cs
door de volgende code:
// 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);
}
De volgende code is een LINQ
query waarmee alle genres uit de database worden opgehaald.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
De SelectList
van genres wordt gemaakt door de verschillende genres te projecteren (we willen niet dat onze selectielijst dubbele genres heeft).
Wanneer de gebruiker naar het item zoekt, blijft de zoekwaarde behouden in het zoekvak.
Zoeken op genre toevoegen aan de indexweergave
Werk als volgt Index.cshtml
bij in Views/Movies/:
@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>
Bekijk de lambda-expressie die wordt gebruikt in de volgende HTML-helper:
@Html.DisplayNameFor(model => model.Movies![0].Title)
In de voorgaande code inspecteert de DisplayNameFor
HTML Helper de eigenschap Title
waarnaar wordt verwezen in de lambda-expressie om de weergavenaam te bepalen. Omdat de lambda-expressie wordt geïnspecteerd in plaats van geëvalueerd, ontvangt u geen toegangsfout wanneer model
, model.Movies
of model.Movies[0]
null
of leeg zijn. Wanneer de lambda-expressie wordt geëvalueerd (bijvoorbeeld @Html.DisplayFor(modelItem => item.Title)
), worden de eigenschapswaarden van het model geëvalueerd. De !
na model.Movies
is de operator null-forgiving-operator, die wordt gebruikt om aan te geven dat Movies
niet null is.
Test de app door te zoeken op genre, op filmtitel en op beide:
In deze sectie voegt u de zoekfunctie toe aan de actiemethode Index
waarmee u films kunt doorzoeken op genre of naam.
Werk de Index
methode in Controllers/MoviesController.cs
bij met de volgende code:
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());
}
Met de volgende regel in de actiemethode Index
wordt een LINQ--query gemaakt om de films te selecteren:
var movies = from m in _context.Movie
select m;
De query is op dit moment alleen gedefinieerd, deze is niet uitgevoerd op de database.
Als de parameter searchString
een tekenreeks bevat, wordt de filmquery gewijzigd om te filteren op de waarde van de zoekreeks:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
De bovenstaande s => s.Title!.ToUpper().Contains(searchString.ToUpper())
code is een Lambda-expressie. Lambdas worden gebruikt in op methoden gebaseerde LINQ--query's als argumenten voor standaardqueryoperatormethoden, zoals de methode Where of Contains
(gebruikt in de bovenstaande code). LINQ-query's worden niet uitgevoerd wanneer ze zijn gedefinieerd of wanneer ze worden gewijzigd door een methode aan te roepen, zoals Where
, Contains
of OrderBy
. In plaats daarvan wordt de uitvoering van query's uitgesteld. Dat betekent dat de evaluatie van een expressie wordt vertraagd totdat de gerealiseerde waarde daadwerkelijk wordt ge curseerd of de methode ToListAsync
wordt aangeroepen. Zie Uitvoering van query'svoor meer informatie over de uitvoering van uitgestelde query's.
Notitie
De methode Contains wordt uitgevoerd in de database, niet in de C#-code. De hoofdlettergevoeligheid voor de query is afhankelijk van de database en de sortering. Op SQL Server wordt Contains
toegewezen aan SQL LIKE-, wat ongevoelig voor hoofdlettergebruik is. SQLite met de standaardsortering is een combinatie van hoofdlettergevoelig en hoofdlettergevoelig INgevoelig, afhankelijk van de query. Zie het volgende voor informatie over het maken van niet-hoofdlettergevoelige SQLite-query's:
Ga naar /Movies/Index
. Voeg een querytekenreeks zoals ?searchString=Ghost
toe aan de URL. De gefilterde films worden weergegeven.
Als u de handtekening van de methode Index
wijzigt om een parameter met de naam id
te hebben, komt de id
parameter overeen met de optionele {id}
tijdelijke aanduiding voor de standaardroutes die zijn ingesteld in Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Wijzig de parameter in id
en wijzig alle voorkomens van searchString
in id
.
De vorige Index
-methode:
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());
}
De bijgewerkte methode Index
met id
parameter:
public async Task<IActionResult> Index(string id)
{
if (_context.Movie == null)
{
return Problem("Entity set 'MvcMovieContext.Movie' is null.");
}
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(id))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(id.ToUpper()));
}
return View(await movies.ToListAsync());
}
U kunt nu de zoektitel doorgeven als routegegevens (een URL-segment) in plaats van als een querytekenreekswaarde.
U kunt echter niet verwachten dat gebruikers de URL telkens wijzigen wanneer ze naar een film willen zoeken. Nu voegt u ui-elementen toe om films te filteren. Als u de handtekening van de methode Index
hebt gewijzigd om te testen hoe u de routegebonden ID
parameter doorgeeft, wijzigt u deze weer zodat er een parameter met de naam searchString
wordt gebruikt:
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());
}
Open het bestand Views/Movies/Index.cshtml
en voeg de <form>
markeringen toe die hieronder zijn gemarkeerd:
@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">
De HTML-<form>
-tag maakt gebruik van de Formuliertag Helper, dus wanneer u het formulier indient, wordt de filtertekenreeks naar de Index
actie van de filmcontroller gestuurd. Sla uw wijzigingen op en test het filter.
Er is geen [HttpPost]
overbelasting van de Index
methode zoals u zou verwachten. U hebt deze niet nodig, omdat de methode de status van de app niet wijzigt, maar alleen gegevens filtert.
U kunt de volgende [HttpPost] Index
methode toevoegen.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
De parameter notUsed
wordt gebruikt om een overbelasting te maken voor de Index
methode. Verderop in de zelfstudie bespreken we dat.
Als u deze methode toevoegt, komt de actie-aanroeper overeen met de [HttpPost] Index
methode en wordt de methode [HttpPost] Index
uitgevoerd zoals wordt weergegeven in de onderstaande afbeelding.
Zelfs als u deze [HttpPost]
versie van de methode Index
toevoegt, is er een beperking in hoe dit allemaal is geïmplementeerd. Stel dat u een bladwijzer wilt maken voor een bepaalde zoekopdracht of dat u een koppeling naar vrienden wilt verzenden waarop ze kunnen klikken om dezelfde gefilterde lijst met films te zien. U ziet dat de URL voor de HTTP POST-aanvraag hetzelfde is als de URL voor de GET-aanvraag (localhost:{PORT}/Movies/Index): er is geen zoekinformatie in de URL. De gegevens van de zoekreeks worden naar de server verzonden als een formulierveldwaarde. U kunt controleren of met de ontwikkelhulpprogramma's van de browser of het uitstekende Fiddler-hulpprogramma. In de onderstaande afbeelding ziet u de ontwikkelhulpprogramma's van de Chrome-browser:
U ziet de zoekparameter en XSRF- token in de verzoekbody. Zoals vermeld in de vorige zelfstudie, genereert de Form Tag Helper een XSRF- antiforgery-token. We wijzigen geen gegevens, dus we hoeven het token niet te valideren in de controllermethode.
Omdat de zoekparameter zich in de aanvraagtekst bevindt en niet de URL, kunt u die zoekgegevens niet vastleggen om een bladwijzer te maken of met anderen te delen. Los dit op door aan te geven dat de aanvraag moet worden gevonden als HTTP GET
in het Views/Movies/Index.cshtml
-bestand.
@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">
Wanneer u nu een zoekopdracht verzendt, bevat de URL de zoekquerytekenreeks. Zoeken gaat ook naar de actiemethode HttpGet Index
, zelfs als u een HttpPost Index
methode hebt.
In de volgende markeringen ziet u de wijziging in de tag form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Zoeken op genre toevoegen
Voeg de volgende MovieGenreViewModel
-klasse toe aan de map Models:
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; }
}
Het filmgenre-weergavemodel bevat:
- Een lijst met films.
- Een
SelectList
met de lijst van genres. Hierdoor kan de gebruiker een genre selecteren in de lijst. -
MovieGenre
, dat het geselecteerde genre bevat. -
SearchString
, die de tekst bevat die gebruikers invoeren in het zoekvak.
Vervang de methode Index
in MoviesController.cs
door de volgende code:
// 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);
}
De volgende code is een LINQ
query waarmee alle genres uit de database worden opgehaald.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
De SelectList
van genres wordt gemaakt door de verschillende genres te projecteren (we willen niet dat onze selectielijst dubbele genres heeft).
Wanneer de gebruiker naar het item zoekt, blijft de zoekwaarde behouden in het zoekvak.
Zoeken op genre toevoegen aan de indexweergave
Werk Index.cshtml
als volgt bij in Views/Movies/:
@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>
Bekijk de lambda-expressie die wordt gebruikt in de volgende HTML-helper:
@Html.DisplayNameFor(model => model.Movies![0].Title)
In de voorgaande code inspecteert de DisplayNameFor
HTML Helper de eigenschap Title
waarnaar wordt verwezen in de lambda-expressie om de weergavenaam te bepalen. Omdat de lambda-expressie wordt geïnspecteerd in plaats van geëvalueerd, ontvangt u geen toegangsfout wanneer model
, model.Movies
of model.Movies[0]
null
of leeg zijn. Wanneer de lambda-expressie wordt geëvalueerd (bijvoorbeeld @Html.DisplayFor(modelItem => item.Title)
), worden de eigenschapswaarden van het model geëvalueerd. De !
na model.Movies
is de null-forgiving operator , die aangeeft dat Movies
niet null is.
Test de app door te zoeken op genre, op filmtitel en op beide:
In deze sectie voegt u de zoekfunctie toe aan de actiemethode Index
waarmee u films kunt doorzoeken op genre of naam.
Werk de Index
methode in Controllers/MoviesController.cs
bij met de volgende code:
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());
}
Met de eerste regel van de actiemethode Index
maakt u een LINQ--query om de films te selecteren:
var movies = from m in _context.Movie
select m;
De query is op dit moment alleen gedefinieerd, deze is niet uitgevoerd op de database.
Als de parameter searchString
een tekenreeks bevat, wordt de filmquery gewijzigd om te filteren op de waarde van de zoekreeks:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.Contains(searchString));
}
De bovenstaande s => s.Title!.Contains(searchString)
code is een lambda-expressie. Lambdas worden gebruikt in op methoden gebaseerde LINQ--query's als argumenten voor standaardqueryoperatormethoden, zoals de methode Where of Contains
(gebruikt in de bovenstaande code). LINQ-query's worden niet uitgevoerd wanneer ze zijn gedefinieerd of wanneer ze worden gewijzigd door een methode aan te roepen, zoals Where
, Contains
of OrderBy
. In plaats daarvan wordt de uitvoering van query's uitgesteld. Dat betekent dat de evaluatie van een uitdrukking wordt uitgesteld totdat de gerealiseerde waarde daadwerkelijk wordt geïtereerd of de methode ToListAsync
wordt aangeroepen. Zie queryuitvoeringvoor meer informatie over de uitvoering van uitgestelde query's.
Opmerking: de methode Contains wordt uitgevoerd op de database, niet in de c#-code die hierboven wordt weergegeven. De hoofdlettergevoeligheid voor de query is afhankelijk van de database en de sortering. Op SQL Server wordt Contains
toegewezen aan SQL LIKE-, dat hoofdletterongevoelig is. In SQLite, met de standaardsortering, is het hoofdlettergevoelig.
Ga naar /Movies/Index
. Voeg een querytekenreeks zoals ?searchString=Ghost
toe aan de URL. De gefilterde films worden weergegeven.
Als u de handtekening van de methode Index
wijzigt om een parameter met de naam id
te hebben, komt de id
parameter overeen met de optionele {id}
tijdelijke aanduiding voor de standaardroutes die zijn ingesteld in Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Wijzig de parameter in id
en wijzig alle exemplaren van searchString
in id
.
De vorige Index
methode:
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());
}
De bijgewerkte methode Index
met id
parameter:
public async Task<IActionResult> Index(string id)
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(id))
{
movies = movies.Where(s => s.Title!.Contains(id));
}
return View(await movies.ToListAsync());
}
U kunt nu de zoektitel doorgeven als routegegevens (een URL-segment) in plaats van als een querytekenreekswaarde.
U kunt echter niet verwachten dat gebruikers de URL telkens wijzigen wanneer ze naar een film willen zoeken. Nu voegt u ui-elementen toe om films te filteren. Als u de handtekening van de methode Index
hebt gewijzigd om te testen hoe u de routegebonden ID
parameter doorgeeft, wijzigt u deze weer zodat er een parameter met de naam searchString
wordt gebruikt:
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());
}
Open het bestand Views/Movies/Index.cshtml
en voeg de <form>
markeringen toe die hieronder zijn gemarkeerd:
@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>
De HTML-<form>
-tag maakt gebruik van de Form Tag Helper, dus wanneer u het formulier verzendt, wordt de filterreeks gepost naar de Index
actie van de filmscontroller. Sla uw wijzigingen op en test het filter.
Er is geen [HttpPost]
overbelasting van de Index
methode zoals u zou verwachten. U hebt deze niet nodig, omdat de methode de status van de app niet wijzigt, maar alleen gegevens filtert.
U kunt de volgende [HttpPost] Index
methode toevoegen.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
De parameter notUsed
wordt gebruikt om een overbelasting te maken voor de Index
methode. Verderop in de zelfstudie bespreken we dat.
Als u deze methode toevoegt, komt de actie-aanroeper overeen met de [HttpPost] Index
methode en wordt de methode [HttpPost] Index
uitgevoerd zoals wordt weergegeven in de onderstaande afbeelding.
Zelfs als u deze [HttpPost]
versie van de methode Index
toevoegt, is er een beperking in hoe dit allemaal is geïmplementeerd. Stel dat u een bladwijzer wilt maken voor een bepaalde zoekopdracht of dat u een koppeling naar vrienden wilt verzenden waarop ze kunnen klikken om dezelfde gefilterde lijst met films te zien. U ziet dat de URL voor de HTTP POST-aanvraag hetzelfde is als de URL voor de GET-aanvraag (localhost:{PORT}/Movies/Index): er is geen zoekinformatie in de URL. De gegevens van de zoekreeks worden naar de server verzonden als een formulierveldwaarde. **
U kunt dat controleren met de ontwikkelaarstools van de browser, of met de uitstekende Fiddler-tool. In de onderstaande afbeelding ziet u de ontwikkelhulpprogramma's van de Chrome-browser:
U ziet de zoekparameter en XSRF- token in de aanvraagbody. Zoals vermeld in de vorige handleiding, genereert de Form Tag Helper een antiforgery-token voor XSRF. We wijzigen geen gegevens, dus we hoeven het token niet te valideren in de controllermethode.
Omdat de zoekparameter zich in de aanvraagtekst bevindt en niet de URL, kunt u die zoekgegevens niet vastleggen om een bladwijzer te maken of met anderen te delen. Los dit op door op te geven dat het verzoek HTTP GET
moet worden gevonden in het Views/Movies/Index.cshtml
bestand.
@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">
Wanneer u nu een zoekopdracht verzendt, bevat de URL de zoekquerytekenreeks. Zoeken gaat ook naar de actiemethode HttpGet Index
, zelfs als u een HttpPost Index
methode hebt.
In de volgende markeringen ziet u de wijziging in de tag form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Zoeken op genre toevoegen
Voeg de volgende MovieGenreViewModel
-klasse toe aan de map Models:
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; }
}
}
Het weergavemodel filmgenre zal bevatten:
- Een lijst met films.
- Een
SelectList
met een genreslijst. Hierdoor kan de gebruiker een genre selecteren in de lijst. -
MovieGenre
, dat het geselecteerde genre bevat. -
SearchString
, die de tekst bevat die gebruikers invoeren in het zoekvak.
Vervang de methode Index
in MoviesController.cs
door de volgende code:
// 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);
}
De volgende code is een LINQ
query waarmee alle genres uit de database worden opgehaald.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
De SelectList
van genres wordt gemaakt door de verschillende genres te projecteren (we willen niet dat onze selectielijst dubbele genres heeft).
Wanneer de gebruiker naar het item zoekt, blijft de zoekwaarde behouden in het zoekvak.
Zoeken op genre toevoegen aan de indexweergave
Werk Index.cshtml
als volgt bij in Views/Movies/:
@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>
Bekijk de lambda-expressie die wordt gebruikt in de volgende HTML-helper:
@Html.DisplayNameFor(model => model.Movies[0].Title)
In de voorgaande code inspecteert de DisplayNameFor
HTML Helper de eigenschap Title
waarnaar wordt verwezen in de lambda-expressie om de weergavenaam te bepalen. Omdat de lambda-expressie wordt geïnspecteerd in plaats van geëvalueerd, ontvangt u geen toegangsfout wanneer model
, model.Movies
of model.Movies[0]
null
of leeg zijn. Wanneer de lambda-expressie wordt geëvalueerd (bijvoorbeeld @Html.DisplayFor(modelItem => item.Title)
), worden de eigenschapswaarden van het model geëvalueerd.
Test de app door te zoeken op genre, op filmtitel en op beide:
In deze sectie voegt u de zoekfunctie toe aan de actiemethode Index
waarmee u films kunt doorzoeken op genre of naam.
Werk de Index
methode in Controllers/MoviesController.cs
bij met de volgende code:
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());
}
Met de eerste regel van de actiemethode Index
maakt u een LINQ--query om de films te selecteren:
var movies = from m in _context.Movie
select m;
De query wordt op dit moment alleen gedefinieerd. De query is niet uitgevoerd op de database.
Als de parameter searchString
een tekenreeks bevat, wordt de filmquery gewijzigd om te filteren op de waarde van de zoekreeks:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
De code hierboven s => s.Title.Contains()
is een lambdautdrukking. Lambdas worden gebruikt in op methoden gebaseerde LINQ--query's als argumenten voor standaardqueryoperatormethoden, zoals de methode Where of Contains
(gebruikt in de bovenstaande code). LINQ-query's worden niet uitgevoerd wanneer ze zijn gedefinieerd of wanneer ze worden gewijzigd door een methode aan te roepen, zoals Where
, Contains
of OrderBy
. In plaats daarvan wordt de uitvoering van queries uitgesteld. Dat betekent dat de evaluatie van een expressie wordt vertraagd totdat de gerealiseerde waarde daadwerkelijk wordt herhaald of de methode ToListAsync
wordt aangeroepen. Zie queryuitvoeringvoor meer informatie over de uitvoering van uitgestelde query's.
Opmerking: de methode Contains wordt uitgevoerd op de database, niet in de c#-code die hierboven wordt weergegeven. De hoofdlettergevoeligheid voor de query is afhankelijk van de database en de sortering. Op SQL Server wordt Contains toegewezen aan SQL LIKE-, wat hoofdletterongevoelig is. In SQLite, met de standaardsortering, is het hoofdlettergevoelig.
Ga naar /Movies/Index
. Voeg een querytekenreeks zoals ?searchString=Ghost
toe aan de URL. De gefilterde films worden weergegeven.
Als u de handtekening van de methode Index
wijzigt om een parameter met de naam id
te hebben, komt de id
parameter overeen met de optionele {id}
tijdelijke aanduiding voor de standaardroutes die zijn ingesteld in Startup.cs
.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Wijzig de parameter naar id
en verander alle voorkomens van searchString
naar id
.
De vorige Index
Methode:
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());
}
De bijgewerkte methode Index
met id
parameter:
public async Task<IActionResult> Index(string id)
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(id))
{
movies = movies.Where(s => s.Title.Contains(id));
}
return View(await movies.ToListAsync());
}
U kunt nu de zoektitel doorgeven als routegegevens (een URL-segment) in plaats van als een querytekenreekswaarde.
U kunt echter niet verwachten dat gebruikers de URL telkens wijzigen wanneer ze naar een film willen zoeken. Nu voegt u ui-elementen toe om films te filteren. Als u de handtekening van de methode Index
hebt gewijzigd om te testen hoe u de routegebonden ID
parameter doorgeeft, wijzigt u deze weer zodat er een parameter met de naam searchString
wordt gebruikt:
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());
}
Open het bestand Views/Movies/Index.cshtml
en voeg de <form>
markeringen toe die hieronder zijn gemarkeerd:
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>
De HTML-<form>
-tag maakt gebruik van de Form Tag Helper, dus zodra u het formulier verzendt, wordt de filtertekenreeks verzonden naar de Index
actie van de filmcontroller. Sla uw wijzigingen op en test het filter.
Er is geen [HttpPost]
overbelasting van de Index
methode zoals u zou verwachten. U hebt deze niet nodig, omdat de methode de status van de app niet wijzigt, maar alleen gegevens filtert.
U kunt de volgende [HttpPost] Index
methode toevoegen.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
De parameter notUsed
wordt gebruikt om een overbelasting te maken voor de Index
methode. Verderop in de zelfstudie bespreken we dat.
Als u deze methode toevoegt, komt de actie-aanroeper overeen met de [HttpPost] Index
methode en wordt de methode [HttpPost] Index
uitgevoerd zoals wordt weergegeven in de onderstaande afbeelding.
Zelfs als u deze [HttpPost]
versie van de methode Index
toevoegt, is er een beperking in hoe dit allemaal is geïmplementeerd. Stel dat u een bladwijzer wilt maken voor een bepaalde zoekopdracht of dat u een koppeling naar vrienden wilt verzenden waarop ze kunnen klikken om dezelfde gefilterde lijst met films te zien. U ziet dat de URL voor de HTTP POST-aanvraag hetzelfde is als de URL voor de GET-aanvraag (localhost:{PORT}/Movies/Index): er is geen zoekinformatie in de URL. De gegevens van de zoekreeks worden naar de server verzonden als een formulierveldwaarde. U kunt dit verifiëren met de ontwikkelhulpprogramma's van de browser of met het uitstekende Fiddler-hulpprogramma. In de onderstaande afbeelding ziet u de ontwikkelhulpprogramma's van de Chrome-browser:
U ziet de zoekparameter en XSRF- token in de verzoekbody. Let op, zoals vermeld in de vorige handleiding, genereert de Form Tag Helper een XSRF- anti-vervalsingstoken. We wijzigen geen gegevens, dus we hoeven het token niet te valideren in de controllermethode.
Omdat de zoekparameter zich in de aanvraagtekst bevindt en niet de URL, kunt u die zoekgegevens niet vastleggen om een bladwijzer te maken of met anderen te delen. Los dit op door aan te geven dat de aanvraag zich in het Views/Movies/Index.cshtml
-bestand moet bevinden op locatie HTTP GET
.
@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)
Wanneer u nu een zoekopdracht verzendt, bevat de URL de zoekquerytekenreeks. Zoeken gaat ook naar de actiemethode HttpGet Index
, zelfs als u een HttpPost Index
methode hebt.
In de volgende markeringen ziet u de wijziging in de tag form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Zoeken op genre toevoegen
Voeg de volgende MovieGenreViewModel
-klasse toe aan de map Models:
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; }
}
}
Het weergavenmodel voor filmgenres bevat:
- Een lijst met films.
- Een
SelectList
met de lijst van genres. Hierdoor kan de gebruiker een genre selecteren in de lijst. -
MovieGenre
, dat het geselecteerde genre bevat. -
SearchString
, die de tekst bevat die gebruikers invoeren in het zoekvak.
Vervang de methode Index
in MoviesController.cs
door de volgende code:
// 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);
}
De volgende code is een LINQ
query waarmee alle genres uit de database worden opgehaald.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
De SelectList
van genres wordt gemaakt door de verschillende genres te projecteren (we willen niet dat onze selectielijst dubbele genres heeft).
Wanneer de gebruiker naar het item zoekt, blijft de zoekwaarde behouden in het zoekvak.
Zoeken op genre toevoegen aan de indexweergave
Werk Index.cshtml
als volgt bij in Views/Movies/:
@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>
Bekijk de lambda-expressie die wordt gebruikt in de volgende HTML-helper:
@Html.DisplayNameFor(model => model.Movies[0].Title)
In de voorgaande code inspecteert de DisplayNameFor
HTML Helper de eigenschap Title
waarnaar wordt verwezen in de lambda-expressie om de weergavenaam te bepalen. Omdat de lambda-expressie wordt geïnspecteerd in plaats van geëvalueerd, ontvangt u geen toegangsfout wanneer model
, model.Movies
of model.Movies[0]
null
of leeg zijn. Wanneer de lambda-expressie wordt geëvalueerd (bijvoorbeeld @Html.DisplayFor(modelItem => item.Title)
), worden de eigenschapswaarden van het model geëvalueerd.
Test de app door te zoeken op genre, op filmtitel en op beide: