原文: "Adding Search" 作者: "Rick Anderson" 翻譯: "魏美娟(初見)" 校對: "謝煬(Kiler)" 、 "孟帥洋(書緣)" 、 "張仁建(第二年.夏)" 在本節中,你可以為 方法添加查詢功能,使其能夠根據電影的 genre 或 name 進行查找。 更新 方法 ...
原文:Adding Search
作者:Rick Anderson
翻譯:魏美娟(初見)
校對:謝煬(Kiler) 、孟帥洋(書緣)、張仁建(第二年.夏)
在本節中,你可以為 Index
方法添加查詢功能,使其能夠根據電影的 genre 或 name 進行查找。
更新 Index
方法來開啟搜索功能:
public async Task<IActionResult> Index(string searchString)
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(await movies.ToListAsync());
}
Index
方法的第一行代碼創建了一個LINQ查詢,用來查找符合條件的電影:
var movies = from m in _context.Movie
select m;
這個查詢 僅僅只是 在這裡被定義出來,但是 並未 在資料庫中執行。
如果 searchString
參數包含一個字元串,movies 查詢將會添加對應查詢過濾條件( 譯者註 本例為 Title 包含 searchString
查詢條件),代碼如下:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString)); //手工高亮
}
代碼中的 s => s.Title.Contains()
是一個Lambda 表達式,Lambda 表達式被用在基於方法的LINQ查詢(例如:上面代碼中的Where方法 或者 Contains
)中當做參數來使用。LINQ 語句在定義或調用類似 Where
、Contains
或者 OrderBy
的方法進行修改的時候不會執行,相反的,查詢會延遲執行,這意味著一個賦值語句直到迭代完成或調用 ToListAsync
方法才具備真正的值。更多關於延時查詢的信息。請參考 查詢執行 。
註意
Contains方法是在資料庫中運行的,並非在上面的 C# 代碼中。在資料庫中,Contains方法被翻譯為不區分大小寫的SQL LIKE腳本。
運行應用程式,並導航到 /Movies/Index
,在 URL 後面添加一個查詢字元串,例如 ?searchString=ghost
,被過濾後的電影列表如下:
如果你修改 Index
方法簽名使得方法包含一個名為 id
的參數,那麼 id
參數將會匹配 Startup.cs 文件中的預設路由中的可選項 {id} 。
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"); //手工高亮
});
你可以使用 rename 操作快速的把 searchString
參數重命名為 id
,在 searchString
上右擊 > Rename 。
會被重命名的代碼會高亮顯示。
修改參數為 id
,其他引用到 searchString
的地方也會自動變更為 id
。
修改前的 Index
方法:
public async Task<IActionResult> Index(string searchString) //手工高亮
{
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(searchString)) //手工高亮
{
movies = movies.Where(s => s.Title.Contains(searchString)); //手工高亮
}
return View(await movies.ToListAsync());
}
修改後的 Index
方法:
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());
}
修改完成以後,我們可以通過路由數據(URL 區塊)來傳遞標題搜索條件而非查詢字元串:
然而,你不能指望用戶每次都通過修改URL來查找電影,因此你需要在界面上幫助他們過濾數據。如果你想要修改 Index
方法的簽名並測試了路由綁定是如何傳遞 ID
參數,現在再把它改成原來的參數 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());
}
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<!--下麵的整個form標簽高亮-->
<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>
標簽使用Form Tag Helper生成,所以當你提交表單的時候,過濾字元串都被傳到了 movies 控制器的 Index
方法。保存你的修改並測試過濾。
然而 Index
方法並沒有你所希望的 [HttpPost]
重載。你也不需要,因為方法並不會修改應用程式的狀態,僅僅只是過濾數據。
你可以添加下麵的 [HttpPost] Index
方法。
[HttpPost] //手工高亮
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
notUsed
參數用創建 Index
方法重載。我們在後續教程中會討論。
如果你添加了這個方法。action 會調用匹配 [HttpPost] Index
的方法, [HttpPost] Index
方法運行結果如下所示。
然而,儘管添加了 [HttpPost]
版的 Index
方法,在實現的時候仍然存在一些局限性。設想你想將一個比較詳細的查詢添加書簽,或者你想將查詢結果以鏈接形式發送給朋友以便於你的朋友可以看到同樣的過濾結果的電影,註意觀察 HTTP POST 請求的時候,URL 是沒有改變的(仍然是 localhost:xxxxx/Movies/Index),這個地址本身不包含查詢信息。現在,查詢信息是作為表單數據發送到伺服器的,你可以通過 F12 開發者工具或者優秀的抓包工具 Fiddler tool。啟動F12 開發者工具。
選擇 http://localhost:xxx/Movies HTTP POST 200 行點擊 Body > Request Body。
你可以在請求正文中看到查詢參數和上一個教程中提到的XSRF令牌。Form Tag Helper生成XSRF 反偽造令牌。我們沒有修改數據,所以無需在控制器方法中驗證令牌。
因為查詢參數在請求正文中而不是 Url 里,所以你在書簽裡面無法保存查詢參數並共用給他人,為瞭解決這個問題,我們必須把請求指定為 HTTP GET
。註意智能感知將幫我們更新標簽。
請註意, <form>
標簽中的專有標記。這種專有標記表示的標簽是由Tag Helpers 支持的。
當你提交檢索的時候,URL 包含查詢條件,如果存在 HttpPost Index
方法,查詢會跳轉到 HttpGet Index
方法。
添加按照 Genre 查詢
在 Models 目錄添加下麵的 MovieGenreViewModel
類:
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; }
}
}
move-genre 視圖模型包含:
- 電影列表
- 包含 genre 列表的SelectList。允許用戶從列表中選擇 genre 。
movieGenre
,包含選中的 genre
用下麵的代碼替換 Index
方法:
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
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();
movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
movieGenreVM.movies = await movies.ToListAsync();
return View(movieGenreVM);
}
下麵代碼是通過 LINQ
語句從資料庫中檢索所有 genre 數據。
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
SelectList
的 genres 通過 Distinct 方法投影查詢創建(我們不想選擇列表中出現重覆的數據)。
movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
在 Index 視圖中添加通過 genre 檢索
<!--手工高亮-->
@model MovieGenreViewModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<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" name="SearchString">
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor( model => model.movies[0].Genre ) <!--手工高亮-->
</th>
<th>
@Html.DisplayNameFor( model => model.movies[0].Price )
</th>
<th>
@Html.DisplayNameFor( model => model.movies[0].ReleaseDate )
</th>
<th>
@Html.DisplayNameFor( model => model.movies[0].Title )
</th>
<th></th>
</tr>
<tbody>
@foreach( var item in Model.movies ) //手工高亮
{
<tr>
<td>
@Html.DisplayFor( modelItem => item.Genre )
</td>
<td>
@Html.DisplayFor( modelItem => item.Price )
</td>
<td>
@Html.DisplayFor( modelItem => item.ReleaseDate )
</td>
<td>
@Html.DisplayFor( modelItem => item.Title )
</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>
測試程式並分別通過 genre 或者 電影標題以及兩個條件同時進行檢索