在本部分中,將向 Index 操作方法添加搜索功能,以實(shí)現(xiàn)按“類型”或“名稱”搜索電影。
使用以下代碼更新 Index 方法:
C#
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());
}
Index 操作方法的第一行創(chuàng)建了 LINQ 查詢用于選擇電影:
C#
var movies = from m in _context.Movie
select m;
此時(shí)僅對查詢進(jìn)行了定義,它還不會針對數(shù)據(jù)庫運(yùn)行。
如果 searchString 參數(shù)包含一個(gè)字符串,電影查詢則會被修改為根據(jù)搜索字符串的值進(jìn)行篩選:
C#
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
上面的 s => s.Title.Contains() 代碼是 Lambda 表達(dá)式。 Lambda 在基于方法的 LINQ 查詢中用作標(biāo)準(zhǔn)查詢運(yùn)算符方法的參數(shù),如 Where 方法或 Contains(上述的代碼中所使用的)。 在對 LINQ 查詢進(jìn)行定義或通過調(diào)用方法(如 Where、Contains 或 OrderBy)進(jìn)行修改后,此查詢不會被執(zhí)行。 相反,會延遲執(zhí)行查詢。 這意味著表達(dá)式的計(jì)算會延遲,直到真正循環(huán)訪問其實(shí)現(xiàn)的值或者調(diào)用 ToListAsync 方法為止。 有關(guān)延遲執(zhí)行查詢的詳細(xì)信息,請參閱Query Execution(查詢執(zhí)行)。
注意:Contains 方法在數(shù)據(jù)庫上運(yùn)行,而不是在上面顯示的 C# 代碼中運(yùn)行。 查詢是否區(qū)分大小寫取決于數(shù)據(jù)庫和排序規(guī)則。 在 SQL Server 上,Contains 映射到 SQL LIKE,這是不區(qū)分大小寫的。 在 SQLite 中,由于使用了默認(rèn)排序規(guī)則,因此需要區(qū)分大小寫。
導(dǎo)航到 /Movies/Index。 將查詢字符串(如 ?searchString=Ghost)追加到 URL。 篩選的電影將顯示出來。
如果將 Index 方法的簽名更改為具有名稱為 id 的參數(shù),則 id 參數(shù)將匹配 Startup.cs 中設(shè)置的默認(rèn)路由的可選 {id} 占位符。
C#
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
將參數(shù)更改為 id,并將出現(xiàn)的所有 searchString 更改為 id。
之前的 Index 方法:
C#
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());
}
更新后帶 id 參數(shù)的 Index 方法:
C#
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());
}
現(xiàn)可將搜索標(biāo)題作為路由數(shù)據(jù)( URL 段)而非查詢字符串值進(jìn)行傳遞。
但是,不能指望用戶在每次要搜索電影時(shí)都修改 URL。 因此需要添加 UI 元素來幫助他們篩選電影。若已更改 Index 方法的簽名,以測試如何傳遞綁定路由的 ID 參數(shù),請改回原樣,使其采用名為 searchString 的參數(shù):
C#
public async Task<IActionResult> Index(string searchString)
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(await movies.ToListAsync());
}
打開“Views/Movies/Index.cshtml”文件,并添加以下突出顯示的 <form> 標(biāo)記:
HTML
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index">
<p>
Title: <input type="text" name="SearchString">
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
此 HTML <form> 標(biāo)記使用表單標(biāo)記幫助程序,因此提交表單時(shí),篩選器字符串會發(fā)布到電影控制器的 Index 操作。 保存更改,然后測試篩選器。
如你所料,不存在 Index 方法的 [HttpPost] 重載。 無需重載,因?yàn)樵摲椒ú桓膽?yīng)用的狀態(tài),僅篩選數(shù)據(jù)。
可添加以下 [HttpPost] Index 方法。
C#
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
notUsed 參數(shù)用于創(chuàng)建 Index 方法的重載。 本教程稍后將對此進(jìn)行探討。
如果添加此方法,則操作調(diào)用程序?qū)⑴c [HttpPost] Index 方法匹配,且將運(yùn)行 [HttpPost] Index 方法,如下圖所示。
但是,即使添加 Index 方法的 [HttpPost] 版本,其實(shí)現(xiàn)方式也受到限制。 假設(shè)你想要將特定搜索加入書簽,或向朋友發(fā)送一個(gè)鏈接,讓他們單擊鏈接即可查看篩選出的相同電影列表。 請注意,HTTP POST 請求的 URL 與 GET 請求的 URL 相同 (localhost:xxxxx/Movies/Index),其中不包含搜索信息。 搜索字符串信息作為表單域值發(fā)送給服務(wù)器。 可使用瀏覽器開發(fā)人員工具或出色的 Fiddler 工具對其進(jìn)行驗(yàn)證。 下圖展示了 Chrome 瀏覽器開發(fā)人員工具:
在請求正文中,可看到搜索參數(shù)和 XSRF 標(biāo)記。 請注意,正如之前教程所述,表單標(biāo)記幫助程序 會生成一個(gè) XSRF 防偽標(biāo)記。 不會修改數(shù)據(jù),因此無需驗(yàn)證控制器方法中的標(biāo)記。
搜索參數(shù)位于請求正文而非 URL 中,因此無法捕獲該搜索信息進(jìn)行書簽設(shè)定或與他人共享。 將通過指定請求為 HTTP GET 進(jìn)行修復(fù):
HTML
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
<p>
Title: <input type="text" name="SearchString">
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
現(xiàn)在提交搜索后,URL 將包含搜索查詢字符串。 即使具備 HttpPost Index 方法,搜索也將轉(zhuǎn)到 HttpGet Index 操作方法。
以下標(biāo)記顯示對 form 標(biāo)記的更改:
HTML
<form asp-controller="Movies" asp-action="Index" method="get">
將以下 MovieGenreViewModel 類添加到“模型”文件夾:
C#
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
namespace MvcMovie.Models
{
public class MovieGenreViewModel
{
public List<Movie> Movies;
public SelectList Genres;
public string MovieGenre { get; set; }
public string SearchString { get; set; }
}
}
“電影流派”視圖模型將包含:
將 MoviesController.cs 中的 Index 方法替換為以下代碼:
C#
// 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);
}
以下代碼是一種 LINQ 查詢,可從數(shù)據(jù)庫中檢索所有流派。
C#
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
通過投影不同的流派創(chuàng)建 SelectList(我們不希望選擇列表中的流派重復(fù))。
當(dāng)用戶搜索某個(gè)項(xiàng)目時(shí),搜索值會保留在搜索框中。
按如下更新 Index.cshtml:
HTML
@model MvcMovie.Models.MovieGenreViewModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movies" asp-action="Index" method="get">
<p>
<select asp-for="MovieGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
Title: <input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Movies[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Movies[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Movies[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Movies[0].Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Movies)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
檢查以下 HTML 幫助程序中使用的 Lambda 表達(dá)式:
@Html.DisplayNameFor(model => model.Movies[0].Title)
在上述代碼中,DisplayNameFor HTML 幫助程序檢查 Lambda 表達(dá)式中引用的 Title 屬性來確定顯示名稱。 由于只檢查但未計(jì)算 Lambda 表達(dá)式,因此當(dāng) model、model.Movies[0] 或 model.Movies為 null 或空時(shí),你不會收到訪問沖突。 對 Lambda 表達(dá)式求值時(shí)(例如,@Html.DisplayFor(modelItem => item.Title)),將求得該模型的屬性值。
通過按流派搜索、按電影標(biāo)題搜索以及按流派和電影標(biāo)題搜索來測試應(yīng)用:
更多建議: