索引:html
目錄索引sql
Adding Search to an ASP.NET Core MVC app數據庫
給程序添加搜索功能express
2017-3-7 7 分鐘閱讀時長 做者 c#
本文內容瀏覽器
根據音樂流派添加搜索mvc
2.Adding search by genre to the Index viewapp
在 Index 視圖添加音樂流派搜索功能async
In this section you add search capability to the Index action method that lets you search movies by genre or name.
在本節咱們將給 Index 方法添加搜索功能,能夠搜索音樂流派或音樂的名字。
Update the Index method with the following code:
用下面的代碼更新 Index 方法:
1 public async Task<IActionResult> Index(string searchString) 2 3 { 4 5 var movies = from m in _context.Movie 6 7 select m; 8 9 10 11 if (!String.IsNullOrEmpty(searchString)) 12 13 { 14 15 movies = movies.Where(s => s.Title.Contains(searchString)); 16 17 } 18 19 20 21 return View(await movies.ToListAsync()); 22 23 }
The first line of the Index action method creates a LINQ query to select the movies:
Index 方法的第一行寫了一個 LINQ 查詢表達式,以查詢出movie數據:
1 var movies = from m in _context.Movie 2 3 select m;
The query is only defined at this point, it has not been run against the database.
這個查詢表達式僅僅在此刻定義了,他是惰性的,不會向數據庫請求執行。
If the searchString parameter contains a string, the movies query is modified to filter on the value of the search string:
searchString 參數包含了一個字符串,movie查詢會去根據這個搜索字符串過濾查詢數據:
1 if (!String.IsNullOrEmpty(id)) 2 3 { 4 5 movies = movies.Where(s => s.Title.Contains(id)); 6 7 }
The s => s.Title.Contains() code above is a Lambda Expression.
上面的 s => s.Title.Contains() 是 Lambda 表達式。
Lambdas are used in method-based LINQqueries as arguments to standard query operator methods such as the Where method or Contains (used in the code above).
Lambda 表達式被用於基於函數爲參數的標準操做函數,如: Where 方法或上面的 Contains 方法。
LINQ queries are not executed when they are defined or when they are modified by calling a method such as Where, Contains or OrderBy.
LINQ 查詢表達式在定義的時候不會執行,當他們被 Where, Contains or OrderBy 修改時也不會執行。
Rather, query execution is deferred.
查詢的執行是被延遲的,它是惰性執行的。
That means that the evaluation of an expression is delayed until its realized value is actually iterated over or the ToListAsyncmethod is called.
意思就是說,表達式的求值會被延遲到真正的去遍歷或者調用了ToListAsync 方法,纔會開始計算表達式的值。
For more information about deferred query execution, see Query Execution.
查看 Query Execution 以獲取更多關於表達式延遲執行的信息。
Note: The Contains method is run on the database, not in the c# code shown above.
筆記: Contains 方法會被翻譯爲sql並在DB中執行,而不是在C#中調用執行。
The case sensitivity on the query depends on the database and the collation.
查詢表達式是依賴於集合或者DB對象的。
On SQL Server, Contains maps to SQL LIKE, which is case insensitive.
在 SQL Server 上,Contains 方法會被智能的映射爲 SQL LIKE 。
In SQLlite, with the default collation, it's case sensitive.
在 SQLlite 中,他會智能的默認矯正。
Navigate to /Movies/Index.
在地址欄導航到 /Movies/Index 。
Append a query string such as ?searchString=Ghost to the URL.
在URL後追加一段像 ?searchString=Ghost 的查詢字符串。
The filtered movies are displayed.
過濾後的movie數據就顯示出來了。
If you change the signature of the Index method to have a parameter named id,
若是你更改 Index 的方法簽名,使其參數名字爲 id ,
the id parameter will match the optional {id} placeholder for the default routes set in Startup.cs.
id 參數將會默認匹配 Startup.cs 文件中的 {id} 可選項參數的佔位符。
1 app.UseMvc(routes => 2 3 { 4 5 routes.MapRoute( 6 7 name: "default", 8 9 template: "{controller=Home}/{action=Index}/{id?}"); 10 11 });
You can quickly rename the searchString parameter to id with the rename command.
你可使用 rename 命令快速的重命名 searchString 參數爲 id 。
Right click on searchString > Rename.
右擊,並選擇 > Rename 菜單。
The rename targets are highlighted.
須要重命名的目標將會所有高亮顯示,以下:
Change the parameter to id and all occurrences of searchString change to id.
改變參數名爲 id ,下面全部出現的 searchString 都會被命名爲 id 。
The previous Index method:
前面的 Index 方法,以下:
1 public async Task<IActionResult> Index(string searchString) 2 3 { 4 5 var movies = from m in _context.Movie 6 7 select m; 8 9 10 11 if (!String.IsNullOrEmpty(searchString)) 12 13 { 14 15 movies = movies.Where(s => s.Title.Contains(searchString)); 16 17 } 18 19 20 21 return View(await movies.ToListAsync()); 22 23 }
The updated Index method with id parameter:
更新後的 Index 方法將帶有名爲 id 的參數,以下:
1 public async Task<IActionResult> Index(string id) 2 3 { 4 5 var movies = from m in _context.Movie 6 7 select m; 8 9 10 11 if (!String.IsNullOrEmpty(id)) 12 13 { 14 15 movies = movies.Where(s => s.Title.Contains(id)); 16 17 } 18 19 20 21 return View(await movies.ToListAsync()); 22 23 }
You can now pass the search title as route data (a URL segment) instead of as a query string value.
你如今就能夠將搜索標題的字符串作爲路由數據的一部分而不是一個查詢字符串使用了。
However, you can't expect users to modify the URL every time they want to search for a movie.
然而,你不會指望用戶每次去更改URL,當他們搜索他們想要的電影的時候。
So now you'll add UI to help them filter movies.
所以,你須要增長UI來幫助他們過濾想要的movies。
If you changed the signature of the Index method to test how to pass the route-bound ID parameter, change it back so that it takes a parameter named searchString:
若是你更改了 Index 方法的簽名來測試路由數據綁定,如今把他改回來,以下:
1 public async Task<IActionResult> Index(string searchString) 2 3 { 4 5 var movies = from m in _context.Movie 6 7 select m; 8 9 10 11 if (!String.IsNullOrEmpty(searchString)) 12 13 { 14 15 movies = movies.Where(s => s.Title.Contains(searchString)); 16 17 } 18 19 20 21 return View(await movies.ToListAsync()); 22 23 }
Open the Views/Movies/Index.cshtml file, and add the <form> markup highlighted below:
打開 Views/Movies/Index.cshtml 文件,而且添加 <form> 標籤,以下面高亮部分所示:
1 ViewData["Title"] = "Index"; 2 3 } 4 5 6 7 <h2>Index</h2> 8 9 10 11 <p> 12 13 <a asp-action="Create">Create New</a> 14 15 </p> 16 17 18 19 <form asp-controller="Movies" asp-action="Index"> 20 21 <p> 22 23 Title: <input type="text" name="SearchString"> 24 25 <input type="submit" value="Filter" /> 26 27 </p> 28 29 </form> 30 31 32 33 <table class="table"> 34 35 <thead>
The HTML <form> tag uses the Form Tag Helper, so when you submit the form, the filter string is posted to the Index action of the movies controller.
<form> 標籤使用了 Form Tag Helper ,所以當你提交表單時,過濾字符串被提交到了Index 方法。
Save your changes and then test the filter.
保存你的修改,而後測試搜索功能。
There's no [HttpPost] overload of the Index method as you might expect.
正如你所料,在 Index 方法上沒有 [HttpPost] 特徵標記類。
You don't need it, because the method isn't changing the state of the app, just filtering data.
你不須要他,由於這個方法不會變動應用的然和狀態,僅僅是查詢了一些數據。
You could add the following [HttpPost] Index method.
你也能夠添加 [HttpPost] ,以下的 Index 方法:
1 [HttpPost] 2 3 public string Index(string searchString, bool notUsed) 4 5 { 6 7 return "From [HttpPost]Index: filter on " + searchString; 8 9 }
The notUsed parameter is used to create an overload for the Index method.
notUsed 參數被用於建立了一個重載的 Index 方法。
We'll talk about that later in the tutorial.
咱們將在後面的教程中對它進行講解。
If you add this method, the action invoker would match the [HttpPost] Index method, and the [HttpPost] Index method would run as shown in the image below.
若是你添加這個方法,action 調用器將會匹配 [HttpPost] Index 方法,而且 [HttpPost] Index 將會執行並返回以下圖所示信息:
However, even if you add this [HttpPost] version of the Index method, there's a limitation in how this has all been implemented.
然而,即便你添加了 [HttpPost] 版本的 Index 方法,這也有一些限制,就是你要怎麼來實現。
Imagine that you want to bookmark a particular search or you want to send a link to friends that they can click in order to see the same filtered list of movies.
猜測你想標記一些詳細的搜索,或者你想給朋友發送一個鏈接,這個鏈接可讓他們看到和你同樣的movies檢索結果。
Notice that the URL for the HTTP POST request is the same as the URL for the GET request (localhost:xxxxx/Movies/Index) -- there's no search information in the URL.
注意到 HTTP POST 請求的URL與GET請求的URL徹底相同,在URL上沒有檢索字符串的數據。
The search string information is sent to the server as a form field value.
檢索用的字符串被作爲表單字段上的值傳遞給服務器。
You can verify that with the browser Developer tools or the excellent Fiddler tool.
你能夠用瀏覽器開發者工具來證明。
The image below shows the Chrome browser Developer tools:
下圖展現了 Chrome 瀏覽器的開發者工具:
You can see the search parameter and XSRF token in the request body.
你能夠在請求體中看到 搜索參數 與 XSRF 令牌。
Note, as mentioned in the previous tutorial, the Form Tag Helper generates an XSRF anti-forgery token.
注意,如前邊兒教程提到的同樣,是 Form Tag Helper 生成了 XSRF 防僞造令牌。
We're not modifying data, so we don't need to validate the token in the controller method.
咱們不去修改數據,所以也不須要在控制器中驗證令牌。
Because the search parameter is in the request body and not the URL, you can't capture that search information to bookmark or share with others.
由於查詢參數在請求體中,而不是在URL中,因此你沒法捕獲查詢信息添加書籤或分享給其餘人。
We'll fix this by specifying the request should be HTTP GET.
咱們修復這點只須要指定請求形式爲 HTTP GET 便可。
Notice how intelliSense helps us update the markup.
注意vs的智能感知如何幫助咱們更新html標記。
Notice the distinctive font in the <form> tag.
注意在 <form> 標籤中的不用的字體顏色。
That distinctive font indicates the tag is supported by Tag Helpers.
不一樣的字體顏色指明瞭哪些受 Tag Helpers 支持。
Now when you submit a search, the URL contains the search query string.
如今,當你提交一個查詢的時候,URL中就在查詢字符串中包含了查詢參數。
Searching will also go to the HttpGet Index action method, even if you have a HttpPost Index method.
查詢將會直接調用 HttpGet Index ,即便已經存在了一個 HttpPost Index 方法。
The following markup shows the change to the form tag:
下面的標記展現了 form 標籤的變動:
1 <form asp-controller="Movies" asp-action="Index" method="get">
Adding Search by genre
添加根據流派進行搜索的功能
Add the following MovieGenreViewModel class to the Models folder:
在 Models 文件夾下添加 MovieGenreViewModel 類:
1 using Microsoft.AspNetCore.Mvc.Rendering; 2 3 using System.Collections.Generic; 4 5 6 7 namespace MvcMovie.Models 8 9 { 10 11 public class MovieGenreViewModel 12 13 { 14 15 public List<Movie> movies; 16 17 public SelectList genres; 18 19 public string movieGenre { get; set; } 20 21 } 22 23 }
The movie-genre view model will contain:
movie-genre 視圖將包含:
一個電影列表。
SelectList 將包含一系列流派,這將使用戶能夠在其中選取流派。
movieGenre ,它包含了被選擇的流派。
Replace the Index method in MoviesController.cs with the following code:
使用下面的代碼替換到 MoviesController.cs 文件中的 Index 方法中:
1 // Requires using Microsoft.AspNetCore.Mvc.Rendering; 2 3 public async Task<IActionResult> Index(string movieGenre, string searchString) 4 5 { 6 7 // Use LINQ to get list of genres. 8 9 IQueryable<string> genreQuery = from m in _context.Movie 10 11 orderby m.Genre 12 13 select m.Genre; 14 15 16 17 var movies = from m in _context.Movie 18 19 select m; 20 21 22 23 if (!String.IsNullOrEmpty(searchString)) 24 25 { 26 27 movies = movies.Where(s => s.Title.Contains(searchString)); 28 29 } 30 31 32 33 if (!String.IsNullOrEmpty(movieGenre)) 34 35 { 36 37 movies = movies.Where(x => x.Genre == movieGenre); 38 39 } 40 41 42 43 var movieGenreVM = new MovieGenreViewModel(); 44 45 movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync()); 46 47 movieGenreVM.movies = await movies.ToListAsync(); 48 49 50 51 return View(movieGenreVM); 52 53 }
The following code is a LINQ query that retrieves all the genres from the database.
下面是一個 LINQ 查詢,他檢索了數據庫中的全部流派:
1 // Use LINQ to get list of genres. 2 3 IQueryable<string> genreQuery = from m in _context.Movie 4 5 orderby m.Genre 6 7 select m.Genre;
The SelectList of genres is created by projecting the distinct genres (we don't want our select list to have duplicate genres).
SelectList 由工程建立並用來給流派去重:
1 movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync())
Adding search by genre to the Index view
將流派查詢添加到 Index 視圖上
Update Index.cshtml as follows:
用下面代碼 更新 Index.cshtml 文件:
1 @model MvcMovie.Models.MovieGenreViewModel 2 3 4 5 @{ 6 7 ViewData["Title"] = "Index"; 8 9 } 10 11 12 13 <h2>Index</h2> 14 15 16 17 <p> 18 19 <a asp-action="Create">Create New</a> 20 21 </p> 22 23 24 25 <form asp-controller="Movies" asp-action="Index" method="get"> 26 27 <p> 28 29 <select asp-for="movieGenre" asp-items="Model.genres"> 30 31 <option value="">All</option> 32 33 </select> 34 35 36 37 Title: <input type="text" name="SearchString"> 38 39 <input type="submit" value="Filter" /> 40 41 </p> 42 43 </form> 44 45 46 47 <table class="table"> 48 49 <thead> 50 51 <tr> 52 53 <th> 54 55 @Html.DisplayNameFor(model => model.movies[0].Title) 56 57 </th> 58 59 <th> 60 61 @Html.DisplayNameFor(model => model.movies[0].ReleaseDate) 62 63 </th> 64 65 <th> 66 67 @Html.DisplayNameFor(model => model.movies[0].Genre) 68 69 </th> 70 71 <th> 72 73 @Html.DisplayNameFor(model => model.movies[0].Price) 74 75 </th> 76 77 <th></th> 78 79 </tr> 80 81 </thead> 82 83 <tbody> 84 85 @foreach (var item in Model.movies) 86 87 { 88 89 <tr> 90 91 <td> 92 93 @Html.DisplayFor(modelItem => item.Title) 94 95 </td> 96 97 <td> 98 99 @Html.DisplayFor(modelItem => item.ReleaseDate) 100 101 </td> 102 103 <td> 104 105 @Html.DisplayFor(modelItem => item.Genre) 106 107 </td> 108 109 <td> 110 111 @Html.DisplayFor(modelItem => item.Price) 112 113 </td> 114 115 <td> 116 117 <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> | 118 119 <a asp-action="Details" asp-route-id="@item.ID">Details</a> | 120 121 <a asp-action="Delete" asp-route-id="@item.ID">Delete</a> 122 123 </td> 124 125 </tr> 126 127 } 128 129 </tbody> 130 131 </table>
Examine the lambda expression used in the following HTML Helper:
檢查下面html中使用的 lambda 表達式:
1 @Html.DisplayNameFor(model => model.movies[0].Title)
In the preceding code, the DisplayNameFor HTML Helper inspects the Title property referenced in the lambda expression to determine the display name.
在上面的代碼中,DisplayNameFor html幫助器函數檢查 Title 屬性,並決定它的界面上的顯示名。
Since the lambda expression is inspected rather than evaluated, you don't receive an access violation when model, model.movies, or model.movies[0] are null or empty.
當 lambda 去檢查而不是去計算時,你不需接受或 model, model.movies, or model.movies[0] are null or empty 。
When the lambda expression is evaluated (for example, @Html.DisplayFor(modelItem => item.Title)), the model's property values are evaluated.
當 lambda 計算時(例如:@Html.DisplayFor(modelItem => item.Title)),模型的屬性值是被計算的。
Test the app by searching by genre, by movie title, and by both.
經過檢索流派或檢索標題來測試本程序。
蒙
2017-08-21 16:18 週一