011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

时间:2022-06-01 14:02:34

索引:

目录索引

Adding Search to an ASP.NET Core MVC app

给程序添加搜索功能

2017-3-7 7 分钟阅读时长 作者

本文内容

1.Adding Search by genre

根据音乐流派添加搜索

2.Adding search by genre to the Index view

在 Index 视图添加音乐流派搜索功能

By Rick Anderson

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 方法:

 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());

 }

C# code

The first line of the Index action method creates a LINQ query to select the movies:

Index 方法的第一行写了一个 LINQ 查询表达式,以查询出movie数据:

 var movies = from m in _context.Movie

              select m;

C# code

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查询会去根据这个搜索字符串过滤查询数据:

 if (!String.IsNullOrEmpty(id))

 {

     movies = movies.Where(s => s.Title.Contains(id));

 }

C# code

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数据就显示出来了。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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} 可选项参数的占位符。

 app.UseMvc(routes =>

 {

     routes.MapRoute(

         name: "default",

         template: "{controller=Home}/{action=Index}/{id?}");

 });

C# code

You can quickly rename the searchString parameter to id with the rename command.

你可以使用 rename 命令快速的重命名 searchString 参数为 id 。

Right click on searchString > Rename.

右击,并选择 > Rename 菜单。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

The rename targets are highlighted.

需要重命名的目标将会全部高亮显示,如下:

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

Change the parameter to id and all occurrences of searchString change to id.

改变参数名为 id ,下面所有出现的 searchString 都会被命名为 id 。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

The previous Index method:

前面的 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());

 }

C# code

The updated Index method with id parameter:

更新后的 Index 方法将带有名为 id  的参数,如下:

 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());

 }

C# code

You can now pass the search title as route data (a URL segment) instead of as a query string value.

你现在就可以将搜索标题的字符串做为路由数据的一部分而不是一个查询字符串使用了。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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 方法的签名来测试路由数据绑定,现在把他改回来,如下:

 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());

 }

C# code

Open the Views/Movies/Index.cshtml file, and add the <form> markup highlighted below:

打开 Views/Movies/Index.cshtml 文件,并且添加 <form> 标签,如下面高亮部分所示:

     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 Code

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.

保存你的修改,然后测试搜索功能。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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 方法:

 [HttpPost]

 public string Index(string searchString, bool notUsed)

 {

     return "From [HttpPost]Index: filter on " + searchString;

 }

C# code

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 将会执行并返回如下图所示信息:

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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 浏览器的开发者工具:

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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标记。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

Notice the distinctive font in the <form> tag.

注意在 <form> 标签中的不用的字体颜色。

That distinctive font indicates the tag is supported by Tag Helpers.

不同的字体颜色指明了哪些受 Tag Helpers 支持。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

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 方法。

011.Adding Search to an ASP.NET Core MVC app --【给程序添加搜索功能】

The following markup shows the change to the form tag:

下面的标记展示了 form 标签的变更:

 <form asp-controller="Movies" asp-action="Index" method="get">

HTML Code

Adding Search by genre

添加根据流派进行搜索的功能

Add the following MovieGenreViewModel class to the Models folder:

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; }

     }

 }

C# Code

The movie-genre view model will contain:

movie-genre 视图将包含:

  • A list of movies.

一个电影列表。

  • A SelectList containing the list of genres. This will allow the user to select a genre from the list.

SelectList 将包含一系列流派,这将使用户可以在其中选取流派。

  • movieGenre, which contains the selected genre.

movieGenre ,它包含了被选择的流派。

Replace the Index method in MoviesController.cs with the following code:

使用下面的代码替换到 MoviesController.cs 文件中的 Index 方法中:

 // Requires using Microsoft.AspNetCore.Mvc.Rendering;

 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();

     movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());

     movieGenreVM.movies = await movies.ToListAsync();

     return View(movieGenreVM);

 }

C# Code

The following code is a LINQ query that retrieves all the genres from the database.

下面是一个 LINQ 查询,他检索了数据库中的所有流派:

 // Use LINQ to get list of genres.

 IQueryable<string> genreQuery = from m in _context.Movie

                                 orderby m.Genre

                                 select m.Genre;

C# Code

The SelectList of genres is created by projecting the distinct genres (we don't want our select list to have duplicate genres).

SelectList 由工程创建并用来给流派去重:

 movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync())

C# Code

Adding search by genre to the Index view

将流派查询添加到 Index 视图上

Update Index.cshtml as follows:

用下面代码 更新 Index.cshtml 文件:

 @model MvcMovie.Models.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">

     <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 Code

Examine the lambda expression used in the following HTML Helper:

检查下面html中使用的 lambda 表达式:

 @Html.DisplayNameFor(model => model.movies[0].Title)

HTML Code

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 周一