Добавление поиска

Rick Anderson

В этом разделе мы добавим в метод действия Index поиск, чтобы вы могли искать ролики по жанру или названию.

Обновите метод действия 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 содержит строку, запрос по роликам изменяется для фильтрации значения в строке поиска с помощью следующего кода:

   if (!String.IsNullOrEmpty(searchString))
   {
       movies = movies.Where(s => s.Title.Contains(searchString));
   }

Код s => s.Title.Contains() - это лямбда выражение. Лямбда выражения используются в LINQ запросах как аргументы для стандартных методов операторов запросов, например Where или Contains. LINQ запросы не выполняются, когда их определяют или изменяют с помощью таких методов как Where, Contains или OrderBy. Выполнение запроса откладывается, пока мы не пройдем циклом по его действительному значению или пока не будет вызван метод ToListAsync. См. Выполнение запросов.

Примечание

Метод Contains работает с БД, а не с c# кодом выше. В БД Contains связывается с SQL LIKE.

Перейдите к /Movies/Index. Добавьте к URL строку запроса ?searchString=ghost. Отображаются отфильтрованные ролики.

../../_images/ghost.png

Если вы меняете сигнатуру метода Index, чтобы у него был параметр id, то параметр id будет соответствовать дополнительному заменителю {id} для роутов по умолчанию, установленному в Startup.cs.

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

Вы можете переименовать параметр searchString на id с помощью команды rename. Кликните правой клавишей мышки по searchString > Rename.

../../_images/rename.png

Выделены переименованные элементы.

../../_images/rename2.png

Измените параметр на id, и все searchString изменятся на id.

../../_images/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 сегмента) название поиска вместо значения строки запроса.

../../_images/g2.png

Однако пользователи не захотят менять URL каждый раз, когда они захотят найти ролик. Так что мы добавим UI, чтобы помочь им фильтровать ролики. Если вы поменяли сигнатуру метода 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());
}

Откройте файл 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">

HTML тег <form> использует Form тег-хелпер, так что когда вы отправляете форму, строка фильтра отправляется действию Index. Сохраните изменения и протестируйте фильтр.

../../_images/filter.png

Здесь нет переопределенного [HttpPost] варианта метода Index, как вы могли ожидать. Вам он не нужен, поскольку метод не меняет состояние приложения, а просто фильтрует данные.

Вы могли бы добавить следующий [HttpPost] Index метод.

[HttpPost]
public string Index(string searchString, bool notUsed)
{
    return "From [HttpPost]Index: filter on " + searchString;
}

Параметр notUsed используется для создания переопределенного варианта метода Index.

Если вы добавляете этот метод, [HttpPost] Index будет запущен, как показано на следующем изображении.

../../_images/fo.png

Однако даже если вы добавите эту [HttpPost] версию метода Index, существует ограничение в том, как все это будет реализовано. Представьте себе, что вы хотите сделать закладку для определенного поиска или отправить ссылку друзьям, кликнув по которой, они могли бы увидеть тот же список отфильтрованных роликов. Обратите внимание, что URL для HTTP POST запроса такой же, что и URL для GET запроса (localhost:xxxxx/Movies/Index) – в URL нет информации по поиску. Информация поисковой строки отправляется на сервер в качестве значения поля формы. Вы можете проверить это с помощью инструментов для разработки F12 или Fiddler. Запустите F12:

Нажмите на строку http://localhost:xxx/Movies HTTP POST 200, а затем нажмите Body > Request Body.

../../_images/f12_rb.png

В теле запроса вы видите параметр поиска и XSRF токен. Обратите внимание, что Form тег-хелпер генерирует XSRF токен. Мы не меняем данные, так что нам не нужно валидировать токен в методе контроллера.

Поскольку параметр поиска находится в теле запроса, а не в URL, вы не сможете передать эту информацию другим или сделать закладку. Мы это исправим, указав, что запрос должен быть HTTP GET. Обратите внимание, что intelliSense помогает нам обновить разметку.

../../_images/int_m.png ../../_images/int_get.png

Обратите внимание на особый шрифт в теге <form>. Этот особый шрифт показывает, что тег поддерживается тег-хелперами.

../../_images/th_font.png

Теперь когда вы отправляете поисковую информацию, URL содержит поисковую строку запроса. Поиск теперь отправляется методу действия HttpGet Index, даже если у вас есть HttpPost Index.

../../_images/search_get.png

Следующая разметка показывает изменения в теге form:

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

Поиск по жанру

Добавьте следующий класс MovieGenreViewModel в папку Models:

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

Модель представления будет содержать:

  • список роликов
  • SelectList, содержащий список жанров. Это позволит пользователю выбрать жанр из списка.
  • movieGenre, содержащий выбранный жанр

Замените код в методе Index следующим:

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

Следующий код - это LINQ запрос, который получает все жанры из БД.

IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

В SelectList находятся жанры из удаленного источника (мы не хотим, чтобы в нашем списке жанры дублировались).

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

Добавление поиска по жанрам в представление Index

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

Протестируйте приложение, проведя поиск по жанру, по названию и объединив эти два критерия.

Поделись хорошей новостью с друзьями!
Следи за новостями!