Del 7, lägg till sökning i en ASP.NET Core MVC-app
Not
Det här är inte den senaste versionen av den här artikeln. För den nuvarande versionen, se .NET 9-versionen av denna artikel.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. För den aktuella versionen, se den .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den aktuella versionen, se .NET 9-versionen av den här artikeln.
I det här avsnittet lägger du till sökfunktionen i åtgärdsmetoden Index
som gör att du kan söka efter filmer efter genre eller namn.
Uppdatera Index
-metoden som finns i Controllers/MoviesController.cs
med följande kod:
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());
}
Följande rad i Index
-åtgärdsmetoden skapar en LINQ- fråga för att välja filmer:
var movies = from m in _context.Movie
select m;
Frågan definieras endast vid denna tidpunkt och har inte körts mot databasen.
Om parametern searchString
innehåller en sträng ändras filmfrågan för att filtrera efter värdet för söksträngen:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
Koden s => s.Title!.ToUpper().Contains(searchString.ToUpper())
ovan är ett Lambda-uttryck. Lambdas används i metodbaserade LINQ frågor som argument till standardmetoder för frågeoperatorer, till exempel Where-metoden eller Contains
(används i koden ovan). LINQ-frågor körs inte när de definieras eller när de ändras genom att anropa en metod som Where
, Contains
eller OrderBy
. I stället skjuts frågekörningen upp. Det innebär att utvärderingen av ett uttryck fördröjs tills dess realiserade värde faktiskt itereras över eller metoden ToListAsync
anropas. För mer information om uppskjuten frågekörning, se Frågekörning.
Anteckning
Metoden Contains körs på databasen, inte i C#-koden. Skiftlägeskänsligheten för frågan beror på databasen och teckenuppsättningen. På SQL Server mappar Contains
till SQL LIKE, vilket inte är skiftlägeskänsligt. SQLite med standardsortering är en blandning av skiftlägeskänsliga och skiftlägesokänsliga INberoende på frågan. Information om hur du gör skiftlägesokänsliga SQLite-frågor finns i följande:
Gå till /Movies/Index
. Lägg till en frågesträng, till exempel ?searchString=Ghost
till URL:en. De filtrerade filmerna visas.
Om du ändrar signaturen för metoden Index
så att den har en parameter med namnet id
matchar parametern id
den valfria {id}
platshållaren för standardvägarna som anges i Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Ändra parametern till id
och ändra alla förekomster av searchString
till id
.
Föregående Index
metod:
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());
}
Den uppdaterade Index
metoden med 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());
}
Nu kan du skicka sökrubriken som routningsdata (ett URL-segment) i stället för som ett frågesträngsvärde.
Du kan dock inte förvänta dig att användarna ändrar URL:en varje gång de vill söka efter en film. Så nu ska du lägga till gränssnittselement som hjälper dem att filtrera filmer. Om du har ändrat signaturen för metoden Index
för att testa hur du skickar den routningsbundna ID
parametern ändrar du tillbaka den så att den tar en parameter med namnet 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());
}
Öppna filen Views/Movies/Index.cshtml
och lägg till <form>
markeringen som är markerad nedan:
@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">
HTML-taggen <form>
använder Form Tag Helper , så när du skickar in formuläret postas filtersträngen till Index
-åtgärden av filmcontroller. Spara ändringarna och testa sedan filtret.
Det finns ingen [HttpPost]
-överladdning av Index
-metoden som du kanske förväntar dig. Du behöver det inte eftersom metoden inte ändrar appens tillstånd, utan bara filtrerar data.
Du kan lägga till följande [HttpPost] Index
metod.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
Parametern notUsed
används för att skapa en överlagring för metoden Index
. Vi kommer att prata om det senare i handledningen.
Om du lägger till den här metoden matchar åtgärdsanroparen metoden [HttpPost] Index
och metoden [HttpPost] Index
skulle köras enligt bilden nedan.
Men även om du lägger till den här [HttpPost]
versionen av Index
-metoden finns det en begränsning i hur allt detta har implementerats. Tänk dig att du vill bokmärke en viss sökning eller att du vill skicka en länk till vänner som de kan klicka på för att se samma filtrerade lista över filmer. Observera att URL:en för HTTP POST-begäran är densamma som URL:en för GET-begäran (localhost:{PORT}/Movies/Index) – det finns ingen sökinformation i URL:en. Söksträngsinformationen skickas till servern som ett formulärfältvärde. Du kan kontrollera att med webbläsarens utvecklarverktyg eller det utmärkta Fiddler-verktyget.
Följande bild visar chrome-webbläsarutvecklarverktygen med Network och Rubriker flikar valda:
Flikarna Network och Payload är markerade för att visa formulärdata:
Du kan se sökparametern och XSRF- token i begärandetexten. Observera, som nämndes i föregående handledning, att Form Tag Helper genererar en XSRF- antiforgery-token. Vi ändrar inte data, så vi behöver inte verifiera token i kontrollantmetoden.
Eftersom sökparametern finns i begärandetexten och inte i URL:en kan du inte samla in den sökinformationen för att bokmärka eller dela med andra. Åtgärda detta genom att specificera att begäran ska vara HTTP GET
i taggen form
som finns i filen 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">
Nu när du skickar en sökning innehåller URL:en sökfrågesträngen. Sökningen går också till åtgärdsmetoden HttpGet Index
, även om du har en HttpPost Index
-metod.
Lägg till sök efter genre
Lägg till följande MovieGenreViewModel
-klass i mappen 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; }
}
Visningsmodellen för filmgenren innehåller:
- En lista över filmer.
- En
SelectList
som innehåller listan över genrer. På så sätt kan användaren välja en genre i listan. -
MovieGenre
, som innehåller den valda genren. -
SearchString
, som innehåller texten som användarna anger i söktextrutan.
Ersätt metoden Index
i MoviesController.cs
med följande kod:
// 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);
}
Följande kod är en LINQ
fråga som hämtar alla genrer från databasen.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
Den SelectList
av genrer skapas genom att projicera distinkta genrer (vi vill inte att vår urvalslista ska ha dubbletter av genrer).
När användaren söker efter objektet behålls sökvärdet i sökrutan.
Lägga till sökning efter genre i indexvyn
Uppdatera Index.cshtml
som finns i Views/Movies/ enligt följande:
@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>
Granska lambda-uttrycket som används i följande HTML-hjälp:
@Html.DisplayNameFor(model => model.Movies![0].Title)
I föregående kod inspekterar DisplayNameFor
HTML Helpern den Title
egenskap som refereras på i lambda-uttrycket för att fastställa visningsnamnet. Eftersom lambda-uttrycket inspekteras i stället för att utvärderas får du ingen åtkomstöverträdelse när model
, model.Movies
eller model.Movies[0]
är null
eller tomma. När lambda-uttrycket utvärderas (till exempel @Html.DisplayFor(modelItem => item.Title)
) utvärderas modellens egenskapsvärden.
!
efter model.Movies
är null-förlåtande operatorn, som används för att deklarera att Movies
inte är null.
Testa appen genom att söka efter genre, efter filmtitel och efter båda:
I det här avsnittet lägger du till sökfunktionen i åtgärdsmetoden Index
som gör att du kan söka efter filmer efter genre eller namn.
Uppdatera Index
-metoden som finns i Controllers/MoviesController.cs
med följande kod:
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());
}
Följande rad i Index
-åtgärdsmetoden skapar en LINQ- fråga för att välja filmer:
var movies = from m in _context.Movie
select m;
Frågan är endast definierad just nu; den har inte körts mot databasen.
Om parametern searchString
innehåller en sträng ändras filmfrågan för att filtrera efter värdet för söksträngen:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
Koden s => s.Title!.ToUpper().Contains(searchString.ToUpper())
ovan är ett Lambda-uttryck. Lambdas används i metodbaserade LINQ frågor som argument till standardmetoder för frågeoperatorer, till exempel Where-metoden eller Contains
(används i koden ovan). LINQ-frågor körs inte när de definieras eller när de ändras genom att anropa en metod som Where
, Contains
eller OrderBy
. I stället skjuts frågekörningen upp. Det innebär att utvärderingen av ett uttryck fördröjs tills dess realiserade värde faktiskt itereras över eller metoden ToListAsync
anropas. Mer information om uppskjuten frågekörning finns i Frågekörning.
Not
Metoden Contains körs på databasen, inte i C#-koden. Skiftlägeskänsligheten för frågan beror på databasen och sorteringen. På SQL Server mappar Contains
till SQL LIKE, vilket är skiftlägesokänsligt. SQLite med standardsortering är en blandning av skiftlägeskänslighet och skiftlägesokänslighet INberoende på frågan. Information om hur du gör skiftlägesokänsliga SQLite-frågor finns i följande:
Gå till /Movies/Index
. Lägg till en frågesträng, till exempel ?searchString=Ghost
till URL:en. De filtrerade filmerna visas.
Om du ändrar signaturen för metoden Index
så att den har en parameter med namnet id
matchar parametern id
den valfria {id}
platshållaren för standardvägarna som anges i Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Ändra parametern till id
och ändra alla förekomster av searchString
till id
.
Föregående Index
metod:
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());
}
Den uppdaterade Index
metoden med 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());
}
Nu kan du skicka sökrubriken som routningsdata (ett URL-segment) i stället för som ett frågesträngsvärde.
Du kan dock inte förvänta dig att användarna ändrar URL:en varje gång de vill söka efter en film. Så nu ska du lägga till gränssnittselement som hjälper dem att filtrera filmer. Om du har ändrat signaturen för metoden Index
för att testa hur du skickar den routningsbundna ID
parametern ändrar du tillbaka den så att den tar en parameter med namnet 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());
}
Öppna filen Views/Movies/Index.cshtml
och lägg till <form>
markeringen som är markerad nedan:
@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">
HTML-<form>
-taggen använder hjälpverktyget formulärtagg, så när du skickar formuläret publiceras filtersträngen till Index
åtgärd för filmkontrollanten. Spara ändringarna och testa sedan filtret.
Det finns ingen [HttpPost]
överlagring av Index
metod som du kan förvänta dig. Du behöver det inte eftersom metoden inte ändrar appens tillstånd, utan bara filtrerar data.
Du kan lägga till följande [HttpPost] Index
metod.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
Parametern notUsed
används för att skapa en överlagring för metoden Index
. Vi pratar om det senare i handledningen.
Om du lägger till den här metoden matchar åtgärdsanroparen metoden [HttpPost] Index
och metoden [HttpPost] Index
skulle köras enligt bilden nedan.
Men även om du lägger till den här [HttpPost]
versionen av Index
-metoden finns det en begränsning i hur allt detta har implementerats. Tänk dig att du vill bokmärke en viss sökning eller att du vill skicka en länk till vänner som de kan klicka på för att se samma filtrerade lista över filmer. Observera att URL:en för HTTP POST-begäran är densamma som URL:en för GET-begäran (localhost:{PORT}/Movies/Index) – det finns ingen sökinformation i URL:en. Söksträngsinformationen skickas till servern som ett formulärfältvärde. Du kan kontrollera att med webbläsarens utvecklarverktyg eller det utmärkta Fiddler-verktyget. Bilden nedan visar Chrome-webbläsarens utvecklarverktyg:
Du kan se sökparametern och XSRF- token i begärandetexten. Observera, som nämnts i den föregående självstudien, att Form Tag Helper genererar en XSRF- antiförfalsknings-token. Vi ändrar inte data, så vi behöver inte verifiera token i kontrollantmetoden.
Eftersom sökparametern finns i begärandetexten och inte i URL:en kan du inte samla in den sökinformationen för att bokmärka eller dela med andra. Åtgärda detta genom att ange att begäran ska HTTP GET
hittas i filen 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">
Nu när du skickar en sökning innehåller URL:en sökfrågesträngen. Sökningen går också till åtgärdsmetoden HttpGet Index
, även om du har en HttpPost Index
-metod.
Följande markering visar ändringen av taggen form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Lägg till sök efter genre
Lägg till följande MovieGenreViewModel
-klass i mappen 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; }
}
Visningsmodellen för filmgenren innehåller:
- En lista över filmer.
- En
SelectList
som innehåller listan över genrer. På så sätt kan användaren välja en genre i listan. -
MovieGenre
, som innehåller den valda genren. -
SearchString
, som innehåller texten som användarna anger i söktextrutan.
Ersätt metoden Index
i MoviesController.cs
med följande kod:
// 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);
}
Följande kod är en LINQ
fråga som hämtar alla genrer från databasen.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
Genrerna i SelectList
skapas genom att projicera distinkta genrer (vi vill inte att vår urvalslista ska ha dubletter av genrer).
När användaren söker efter objektet behålls sökvärdet i sökrutan.
Lägga till sökning efter genre i indexvyn
Uppdatera Index.cshtml
som finns i Views/Movies/ enligt följande:
@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>
Granska lambda-uttrycket som används i följande HTML-hjälp:
@Html.DisplayNameFor(model => model.Movies![0].Title)
I den föregående koden inspekterar DisplayNameFor
HTML-hjälpare den Title
-egenskap som refereras i lambda-uttrycket för att fastställa visningsnamnet. Eftersom lambda-uttrycket inspekteras i stället för att utvärderas får du ingen åtkomstöverträdelse när model
, model.Movies
eller model.Movies[0]
är null
eller tomma. När lambda-uttrycket utvärderas (till exempel @Html.DisplayFor(modelItem => item.Title)
) utvärderas modellens egenskapsvärden.
!
efter model.Movies
är null-förlåtande operatorn, som används för att deklarera att Movies
inte är null.
Testa appen genom att söka efter genre, efter filmtitel och efter båda:
I det här avsnittet lägger du till sökfunktionen i åtgärdsmetoden Index
som gör att du kan söka efter filmer efter genre eller namn.
Uppdatera Index
-metoden som finns i Controllers/MoviesController.cs
med följande kod:
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());
}
Följande rad i Index
-åtgärdsmetoden skapar en LINQ- fråga för att välja filmer:
var movies = from m in _context.Movie
select m;
Förfrågan är endast definierad för tillfället, den har inte körts mot databasen ännu.
Om parametern searchString
innehåller en sträng ändras filmfrågan för att filtrera efter värdet för söksträngen:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.ToUpper().Contains(searchString.ToUpper()));
}
Koden s => s.Title!.ToUpper().Contains(searchString.ToUpper())
ovan är ett Lambda-uttryck. Lambdas används i metodbaserade LINQ frågor som argument till standardmetoder för frågeoperatorer, till exempel Where-metoden eller Contains
(används i koden ovan). LINQ-frågor körs inte när de definieras eller när de ändras genom att anropa en metod som Where
, Contains
eller OrderBy
. I stället skjuts frågekörningen upp. Det innebär att utvärderingen av ett uttryck fördröjs tills dess realiserade värde faktiskt itereras över eller metoden ToListAsync
anropas. För mer information om uppskjuten frågekörning, se Frågekörning.
Not
Metoden Contains körs på databasen, inte i C#-koden. Skiftlägeskänsligheten för frågeparametrarna beror på databasen och sorteringsordningen. På SQL Server mappar Contains
till SQL LIKE, vilket är skiftlägesokänsligt. SQLite med standardsortering är en blandning av skiftlägeskänsliga och skiftlägesokänsliga IN, beroende på frågan. Information om hur du gör skiftlägesokänsliga SQLite-frågor finns i följande:
Gå till /Movies/Index
. Lägg till en frågesträng, till exempel ?searchString=Ghost
till URL:en. De filtrerade filmerna visas.
Om du ändrar signaturen för metoden Index
så att den har en parameter med namnet id
matchar parametern id
den valfria {id}
platshållaren för standardvägarna som anges i Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Ändra parametern till id
och ändra alla förekomster av searchString
till id
.
Föregående Index
metod:
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());
}
Den uppdaterade Index
metoden med 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());
}
Nu kan du skicka sökrubriken som routningsdata (ett URL-segment) i stället för som ett frågesträngsvärde.
Du kan dock inte förvänta dig att användarna ändrar URL:en varje gång de vill söka efter en film. Så nu ska du lägga till gränssnittselement som hjälper dem att filtrera filmer. Om du har ändrat signaturen för metoden Index
för att testa hur du skickar den routningsbundna ID
parametern ändrar du tillbaka den så att den tar en parameter med namnet 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());
}
Öppna filen Views/Movies/Index.cshtml
och lägg till <form>
markeringen som är markerad nedan:
@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">
HTML-<form>
-taggen använder Form Tag Helper , så när du skickar formuläret skickas filtersträngen till åtgärden Index
i filmkontrollern. Spara ändringarna och testa sedan filtret.
Det finns ingen [HttpPost]
överlagring av Index
-metoden som du kan förvänta dig. Du behöver det inte eftersom metoden inte ändrar appens tillstånd, utan bara filtrerar data.
Du kan lägga till följande [HttpPost] Index
metod.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
Parametern notUsed
används för att skapa en överlagring för metoden Index
. Vi kommer att prata om det senare i handledningen.
Om du lägger till den här metoden matchar åtgärdsanroparen metoden [HttpPost] Index
och metoden [HttpPost] Index
skulle köras enligt bilden nedan.
Men även om du lägger till den här [HttpPost]
versionen av Index
-metoden finns det en begränsning i hur allt detta har implementerats. Tänk dig att du vill bokmärke en viss sökning eller att du vill skicka en länk till vänner som de kan klicka på för att se samma filtrerade lista över filmer. Observera att URL:en för HTTP POST-begäran är densamma som URL:en för GET-begäran (localhost:{PORT}/Movies/Index) – det finns ingen sökinformation i URL:en. Söksträngsinformationen skickas till servern som ett formulärfältvärde. Du kan kontrollera att med webbläsarens utvecklarverktyg eller det utmärkta Fiddler-verktyget. Bilden nedan visar Chrome-webbläsarens utvecklarverktyg:
Du kan se sökparametern och XSRF- token i begärandetexten. Observera, som du nämnde i föregående självstudie, att Form Tag Helper genererar en XSRF- antiforgery-token. Vi ändrar inte data, så vi behöver inte verifiera token i kontrollantmetoden.
Eftersom sökparametern finns i begärandetexten och inte i URL:en kan du inte samla in den sökinformationen för att bokmärka eller dela med andra. Åtgärda detta genom att specificera att begäran blir HTTP GET
hittad i filen 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">
Nu när du skickar en sökning innehåller URL:en sökfrågesträngen. Sökningen går också till åtgärdsmetoden HttpGet Index
, även om du har en HttpPost Index
-metod.
Följande markering visar ändringen av taggen form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Lägg till sök efter genre
Lägg till följande MovieGenreViewModel
-klass i mappen 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; }
}
Visningsmodellen för filmgenren innehåller:
- En lista över filmer.
- En
SelectList
som innehåller listan över genrer. På så sätt kan användaren välja en genre i listan. -
MovieGenre
, som innehåller den valda genren. -
SearchString
, som innehåller texten som användarna anger i söktextrutan.
Ersätt metoden Index
i MoviesController.cs
med följande kod:
// 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);
}
Följande kod är en LINQ
fråga som hämtar alla genrer från databasen.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
Det SelectList
av genrer skapas genom att projicera distinkta genrer (vi vill inte att vår urvalslista ska ha dubbla genrer).
När användaren söker efter objektet behålls sökvärdet i sökrutan.
Lägga till sökning efter genre i indexvyn
Uppdatera Index.cshtml
som finns i Views/Movies/ enligt följande:
@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>
Granska lambda-uttrycket som används i följande HTML-hjälp:
@Html.DisplayNameFor(model => model.Movies![0].Title)
I föregående kod inspekterar DisplayNameFor
HTML-hjälpmedlet Title
egenskap som refereras i lambda-uttrycket, för att fastställa visningsnamnet. Eftersom lambda-uttrycket inspekteras i stället för att utvärderas får du ingen åtkomstöverträdelse när model
, model.Movies
eller model.Movies[0]
är null
eller tomma. När lambda-uttrycket utvärderas (till exempel @Html.DisplayFor(modelItem => item.Title)
) utvärderas modellens egenskapsvärden.
!
efter model.Movies
är null-förlåtande operatorn, som används för att deklarera att Movies
inte är null.
Testa appen genom att söka efter genre, efter filmtitel och efter båda:
I det här avsnittet lägger du till sökfunktionen i åtgärdsmetoden Index
som gör att du kan söka efter filmer efter genre eller namn.
Uppdatera Index
-metoden som finns i Controllers/MoviesController.cs
med följande kod:
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());
}
Den första raden i Index
-åtgärdsmetoden skapar en LINQ- fråga för att välja filmerna:
var movies = from m in _context.Movie
select m;
Frågan definieras endast för tillfället, den har inte körts mot databasen.
Om parametern searchString
innehåller en sträng ändras filmfrågan för att filtrera efter värdet för söksträngen:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title!.Contains(searchString));
}
Koden s => s.Title!.Contains(searchString)
ovan är ett Lambda-uttryck. Lambdas används i metodbaserade LINQ frågor som argument till standardmetoder för frågeoperatorer, till exempel Where-metoden eller Contains
(används i koden ovan). LINQ-frågor körs inte när de definieras eller när de ändras genom att anropa en metod som Where
, Contains
eller OrderBy
. I stället skjuts frågekörningen upp. Det innebär att utvärderingen av ett uttryck fördröjs tills dess realiserade värde faktiskt itereras över eller metoden ToListAsync
anropas. Mer information om uppskjuten frågekörning finns i Frågekörning.
Obs! Metoden Contains körs på databasen, inte i c#-koden som visas ovan. Skiftlägeskänsligheten för frågan beror på databasen och teckenuppsättningen. På SQL Server motsvarar Contains
SQL LIKE, vilket är skiftlägesokänsligt. I SQLite, med standardsortering, är det skiftlägeskänsligt.
Gå till /Movies/Index
. Lägg till en frågesträng, till exempel ?searchString=Ghost
till URL:en. De filtrerade filmerna visas.
Om du ändrar signaturen för metoden Index
så att den har en parameter med namnet id
matchar parametern id
den valfria {id}
platshållaren för standardvägarna som anges i Program.cs
.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Ändra parametern till id
och ändra alla förekomster av searchString
till id
.
Föregående Index
metod:
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());
}
Den uppdaterade Index
metoden med 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());
}
Nu kan du skicka sökrubriken som routningsdata (ett URL-segment) i stället för som ett frågesträngsvärde.
Du kan dock inte förvänta dig att användarna ändrar URL:en varje gång de vill söka efter en film. Så nu ska du lägga till gränssnittselement som hjälper dem att filtrera filmer. Om du har ändrat signaturen för metoden Index
för att testa hur du skickar den routningsbundna ID
parametern ändrar du tillbaka den så att den tar en parameter med namnet 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());
}
Öppna filen Views/Movies/Index.cshtml
och lägg till <form>
markeringen som är markerad nedan:
@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>
HTML-<form>
-taggen använder Formulärtaggs-hjälparen , så när du skickar formuläret publiceras filtersträngen till Index
-åtgärden för filmkontrollanten. Spara ändringarna och testa sedan filtret.
Det finns ingen [HttpPost]
överlag av Index
-metoden som du kanske förväntar dig. Du behöver det inte eftersom metoden inte ändrar appens tillstånd, utan bara filtrerar data.
Du kan lägga till följande [HttpPost] Index
metod.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
Parametern notUsed
används för att skapa en överlagring för metoden Index
. Vi kommer att prata om det senare i självstudien.
Om du lägger till den här metoden matchar åtgärdsanroparen metoden [HttpPost] Index
och metoden [HttpPost] Index
skulle köras enligt bilden nedan.
Men även om du lägger till den här [HttpPost]
versionen av Index
-metoden finns det en begränsning i hur allt detta har implementerats. Tänk dig att du vill bokmärke en viss sökning eller att du vill skicka en länk till vänner som de kan klicka på för att se samma filtrerade lista över filmer. Observera att URL:en för HTTP POST-begäran är densamma som URL:en för GET-begäran (localhost:{PORT}/Movies/Index) – det finns ingen sökinformation i URL:en. Söksträngsinformationen skickas till servern som ett formulärfältvärde. Du kan kontrollera att med webbläsarens utvecklarverktyg eller det utmärkta Fiddler-verktyget. Bilden nedan visar Chrome-webbläsarens utvecklarverktyg:
Du kan se sökparametern och XSRF- token i begärandetexten. Observera, som nämnts i föregående handledning, att Form Tag Helper genererar en XSRF antiförfalskningstoken. Vi ändrar inte data, så vi behöver inte verifiera token i kontrollantmetoden.
Eftersom sökparametern finns i begärandetexten och inte i URL:en kan du inte samla in den sökinformationen för att bokmärka eller dela med andra. Åtgärda detta genom att specificera att begäran HTTP GET
ska hittas i filen 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">
Nu när du skickar en sökning innehåller URL:en sökfrågesträngen. Sökningen går också till åtgärdsmetoden HttpGet Index
, även om du har en HttpPost Index
-metod.
Följande markering visar ändringen av taggen form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Lägg till sök efter genre
Lägg till följande MovieGenreViewModel
-klass i mappen 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; }
}
}
Visningsmodellen för filmgenren innehåller:
- En lista över filmer.
- En
SelectList
som innehåller listan över genrer. På så sätt kan användaren välja en genre i listan. -
MovieGenre
, som innehåller den valda genren. -
SearchString
, som innehåller texten som användarna anger i söktextrutan.
Ersätt metoden Index
i MoviesController.cs
med följande kod:
// 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);
}
Följande kod är en LINQ
fråga som hämtar alla genrer från databasen.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
Den SelectList
av genrer skapas genom att projicera distinkta genrer (vi vill inte att vår urvalslista ska ha dubbletter av genrer).
När användaren söker efter objektet behålls sökvärdet i sökrutan.
Lägga till sökning efter genre i indexvyn
Uppdatera Index.cshtml
som finns i Views/Movies/ enligt följande:
@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>
Granska lambda-uttrycket som används i följande HTML-hjälp:
@Html.DisplayNameFor(model => model.Movies[0].Title)
I föregående kod inspekterar DisplayNameFor
HTML-hjälpen den Title
egenskap som refereras i lambda-uttrycket för att fastställa visningsnamnet. Eftersom lambda-uttrycket inspekteras i stället för att utvärderas får du ingen åtkomstöverträdelse när model
, model.Movies
eller model.Movies[0]
är null
eller tomma. När lambda-uttrycket utvärderas (till exempel @Html.DisplayFor(modelItem => item.Title)
) utvärderas modellens egenskapsvärden.
Testa appen genom att söka efter genre, efter filmtitel och efter båda:
I det här avsnittet lägger du till sökfunktionen i åtgärdsmetoden Index
som gör att du kan söka efter filmer efter genre eller namn.
Uppdatera Index
-metoden som finns i Controllers/MoviesController.cs
med följande kod:
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());
}
Den första raden i Index
-åtgärdsmetoden skapar en LINQ- fråga för att välja filmerna:
var movies = from m in _context.Movie
select m;
Förfrågan är endast definierad vid den här tidpunkten, den har inte körts mot databasen.
Om parametern searchString
innehåller en sträng ändras filmfrågan för att filtrera efter värdet för söksträngen:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
Koden s => s.Title.Contains()
ovan är ett Lambda-uttryck. Lambdas används i metodbaserade LINQ frågor som argument till standardmetoder för frågeoperatorer, till exempel Where-metoden eller Contains
(används i koden ovan). LINQ-frågor körs inte när de definieras eller när de ändras genom att anropa en metod som Where
, Contains
eller OrderBy
. I stället skjuts frågekörningen upp. Det innebär att utvärderingen av ett uttryck fördröjs tills dess realiserade värde faktiskt itereras över eller metoden ToListAsync
anropas. För mer information om uppskjuten frågekörning, se Frågekörning.
Obs! Metoden Contains körs på databasen, inte i c#-koden som visas ovan. Skiftlägeskänsligheten för frågan beror på databasen och teckenuppsättningen. På SQL Server mappar Contains till SQL LIKE, vilket är skiftlägesokänsligt. I SQLite, med standardsortering, är det skiftlägeskänsligt.
Gå till /Movies/Index
. Lägg till en frågesträng, till exempel ?searchString=Ghost
till URL:en. De filtrerade filmerna visas.
Om du ändrar signaturen för metoden Index
så att den har en parameter med namnet id
matchar parametern id
den valfria {id}
platshållaren för standardvägarna som anges i Startup.cs
.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Ändra parametern till id
och alla förekomster av searchString
ändras till id
.
Föregående Index
metod:
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());
}
Den uppdaterade Index
metoden med 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());
}
Nu kan du skicka sökrubriken som routningsdata (ett URL-segment) i stället för som ett frågesträngsvärde.
Du kan dock inte förvänta dig att användarna ändrar URL:en varje gång de vill söka efter en film. Så nu ska du lägga till gränssnittselement som hjälper dem att filtrera filmer. Om du har ändrat signaturen för metoden Index
för att testa hur du skickar den routningsbundna ID
parametern ändrar du tillbaka den så att den tar en parameter med namnet 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());
}
Öppna filen Views/Movies/Index.cshtml
och lägg till <form>
markeringen som är markerad nedan:
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>
HTML-<form>
-taggen använder hjälpverktyget för -formulärtagg, så när du skickar formuläret skickas filtersträngen till Index
-åtgärden i filmkontrollen. Spara ändringarna och testa sedan filtret.
Det finns ingen [HttpPost]
överlagring av Index
metod som du kan förvänta dig. Du behöver det inte eftersom metoden inte ändrar appens tillstånd, utan bara filtrerar data.
Du kan lägga till följande [HttpPost] Index
metod.
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
Parametern notUsed
används för att skapa en överlagring för metoden Index
. Vi kommer att prata om det senare i handledningen.
Om du lägger till den här metoden matchar åtgärdsanroparen metoden [HttpPost] Index
och metoden [HttpPost] Index
skulle köras enligt bilden nedan.
Men även om du lägger till den här [HttpPost]
versionen av Index
-metoden finns det en begränsning i hur allt detta har implementerats. Tänk dig att du vill bokmärke en viss sökning eller att du vill skicka en länk till vänner som de kan klicka på för att se samma filtrerade lista över filmer. Observera att URL:en för HTTP POST-begäran är densamma som URL:en för GET-begäran (localhost:{PORT}/Movies/Index) – det finns ingen sökinformation i URL:en. Söksträngsinformationen skickas till servern som ett formulärfältvärde. Du kan kontrollera att med webbläsarens utvecklarverktyg eller det utmärkta Fiddler-verktyget. Bilden nedan visar Chrome-webbläsarens utvecklarverktyg:
Du kan se sökparametern och XSRF- token i begärandetexten. Observera, som nämnts i föregående handledning, att Form Tag Helper genererar en XSRF- antiforgery-token. Vi ändrar inte data, så vi behöver inte verifiera token i kontrollantmetoden.
Eftersom sökparametern finns i begärandetexten och inte i URL:en kan du inte samla in den sökinformationen för att bokmärka eller dela med andra. Åtgärda detta genom att ange att begäran ska HTTP GET
hittas i filen 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)
Nu när du skickar en sökning innehåller URL:en sökfrågesträngen. Sökningen går också till åtgärdsmetoden HttpGet Index
, även om du har en HttpPost Index
-metod.
Följande markering visar ändringen av taggen form
:
<form asp-controller="Movies" asp-action="Index" method="get">
Lägg till sök efter genre
Lägg till följande MovieGenreViewModel
-klass i mappen 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; }
}
}
Visningsmodellen för filmgenren innehåller:
- En lista över filmer.
- En
SelectList
som innehåller listan över genrer. På så sätt kan användaren välja en genre i listan. -
MovieGenre
, som innehåller den valda genren. -
SearchString
, som innehåller texten som användarna anger i söktextrutan.
Ersätt metoden Index
i MoviesController.cs
med följande kod:
// 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);
}
Följande kod är en LINQ
fråga som hämtar alla genrer från databasen.
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
Den SelectList
av genrer skapas genom att projicera distinkta genrer (vi vill inte att vår urvalslista ska ha dubbletter av genrer).
När användaren söker efter objektet behålls sökvärdet i sökrutan.
Lägga till sökning efter genre i indexvyn
Uppdatera Index.cshtml
som finns i Views/Movies/ enligt följande:
@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>
Granska lambda-uttrycket som används i följande HTML-hjälp:
@Html.DisplayNameFor(model => model.Movies[0].Title)
I föregående kod inspekterar DisplayNameFor
HTML-hjälpen den Title
egenskap som refereras i lambda-uttrycket för att fastställa visningsnamnet. Eftersom lambda-uttrycket inspekteras i stället för att utvärderas får du ingen åtkomstöverträdelse när model
, model.Movies
eller model.Movies[0]
är null
eller tomma. När lambda-uttrycket utvärderas (till exempel @Html.DisplayFor(modelItem => item.Title)
) utvärderas modellens egenskapsvärden.
Testa appen genom att söka efter genre, efter filmtitel och efter båda:
ASP.NET Core