將搜索添加到 ASP.NET Core MVC 應(yīng)用

2019-04-17 08:57 更新

在本部分中,將向 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)行傳遞。

索引視圖,顯示添加到 URL 的“ghost”一詞以及返回的兩部電影(Ghostbusters 和 Ghostbusters 2)的電影列表

但是,不能指望用戶在每次要搜索電影時(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 操作。 保存更改,然后測試篩選器。

顯示標(biāo)題篩選器文本框中鍵入了 ghost 一詞的索引視圖

如你所料,不存在 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 方法,如下圖所示。

顯示“來自 HttpPost 索引: 篩選 ghost”應(yīng)用程序響應(yīng)的瀏覽器窗口

但是,即使添加 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ā)人員工具:

Microsoft Edge 中開發(fā)人員工具的“網(wǎng)絡(luò)”選項(xiàng)卡,顯示了 ghost 的 searchString 值的請求正文

在請求正文中,可看到搜索參數(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 操作方法。

URL 中顯示 searchString=ghost 且返回了 Ghostbusters 和 Ghostbusters 2 的瀏覽器窗口包含 ghost 一詞

以下標(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; }
    }
}

“電影流派”視圖模型將包含:

  • 電影列表。
  • 包含流派列表的 SelectList。 這使用戶能夠從列表中選擇一種流派。
  • 包含所選流派的 MovieGenre。
  • SearchString包含用戶在搜索文本框中輸入的文本。

將 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)用:

顯示 https://localhost:5001/Movies?MovieGenre=Comedy&SearchString=2 結(jié)果的瀏覽器窗口


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號