ASP.NET Core 中文文檔 第二章 指南(4.7)添加搜索


原文:Adding Search
作者:Rick Anderson
翻譯:魏美娟(初見)
校對:謝煬(Kiler) 、孟帥洋(書緣)張仁建(第二年.夏)

在本節中,你可以為 Index 方法添加查詢功能,使其能夠根據電影的 genrename 進行查找。

更新 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 語句在定義或調用類似 WhereContains 或者 OrderBy 的方法進行修改的時候不會執行,相反的,查詢會延遲執行,這意味着一個賦值語句直到迭代完成或調用 ToListAsync 方法才具備真正的值。更多關於延時查詢的信息。請參考 查詢執行

注意
Contains方法是在數據庫中運行的,並非在上面的 C# 代碼中。在數據庫中,Contains方法被翻譯為不區分大小寫的SQL LIKE腳本。

運行應用程序,並導航到 /Movies/Index,在 URL 后面添加一個查詢字符串,例如 ?searchString=ghost,被過濾后的電影列表如下:

search/_static/ghost.png

如果你修改 Index 方法簽名使得方法包含一個名為 id 的參數,那么 id 參數將會匹配 Startup.cs 文件中的默認路由中的可選項 {id} 。

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}"); //手工高亮
});

你可以使用 rename 操作快速的把 searchString 參數重命名為 id,在 searchString 上右擊 > Rename

search/_static/rename.png

會被重命名的代碼會高亮顯示。

search/_static/rename2.png

修改參數為 id ,其他引用到 searchString 的地方也會自動變更為 id

search/_static/rename3.png

修改前的 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 區塊)來傳遞標題搜索條件而非查詢字符串:

search/_static/g2.png

然而,你不能指望用戶每次都通過修改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 方法。保存你的修改並測試過濾。

search/_static/filter.png

然而 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 方法運行結果如下所示。

search/_static/fo.png

然而,盡管添加了 [HttpPost] 版的 Index 方法,在實現的時候仍然存在一些局限性。設想你想將一個比較詳細的查詢添加書簽,或者你想將查詢結果以鏈接形式發送給朋友以便於你的朋友可以看到同樣的過濾結果的電影,注意觀察 HTTP POST 請求的時候,URL 是沒有改變的(仍然是 localhost:xxxxx/Movies/Index),這個地址本身不包含查詢信息。現在,查詢信息是作為表單數據發送到服務器的,你可以通過 F12 開發者工具或者優秀的抓包工具 Fiddler tool。啟動F12 開發者工具

選擇 http://localhost:xxx/Movies HTTP POST 200 行點擊 Body > Request Body

search/_static/f12_rb.png

你可以在請求正文中看到查詢參數和上一個教程中提到的XSRF令牌。Form Tag Helper生成XSRF 反偽造令牌。我們沒有修改數據,所以無需在控制器方法中驗證令牌。

因為查詢參數在請求正文中而不是 Url 里,所以你在書簽里面無法保存查詢參數並共享給他人,為了解決這個問題,我們必須把請求指定為 HTTP GET。注意智能感知將幫我們更新標簽。

search/_static/int_m.png

search/_static/int_get.png

請注意, <form> 標簽中的專有標記。這種專有標記表示的標簽是由Tag Helpers 支持的。

search/_static/th_font.png

當你提交檢索的時候,URL 包含查詢條件,如果存在 HttpPost Index 方法,查詢會跳轉到 HttpGet Index 方法。

search/_static/search_get.png

添加按照 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 或者 電影標題以及兩個條件同時進行檢索

返回目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM