Dodawanie wyszukiwania do aplikacji ASP.NET Core MVCAdd search to an ASP.NET Core MVC app

Przez Rick AndersonBy Rick Anderson

W tej sekcji możesz dodać możliwości wyszukiwania do Index metody akcji, która umożliwia wyszukiwanie filmów przez gatunku lub nazwa.In this section, you add search capability to the Index action method that lets you search movies by genre or name.

Aktualizacja Index metoda następującym kodem:Update the Index method with the following code:

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

W pierwszym wierszu Index tworzy metody akcji LINQ zapytanie, aby wybrać filmów:The first line of the Index action method creates a LINQ query to select the movies:

var movies = from m in _context.Movie
             select m;

Zapytanie jest tylko zdefiniowane w tym momencie, ma ona nie zostały uruchomione dla bazy danych.The query is only defined at this point, it has not been run against the database.

Jeśli searchString parametru zawiera ciąg, zapytanie filmy zostanie zmodyfikowany na potrzeby filtrowania na wartość ciągu wyszukiwania:If the searchString parameter contains a string, the movies query is modified to filter on the value of the search string:

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

s => s.Title.Contains() Powyższy kod jest wyrażenia Lambda.The s => s.Title.Contains() code above is a Lambda Expression. Wyrażenia lambda są używane w oparte na metodzie LINQ jako argumenty do standardowych metod operatorów kwerendy takich zapytań jak gdzie metody lub Contains (używane w powyższym kodzie).Lambdas are used in method-based LINQ queries as arguments to standard query operator methods such as the Where method or Contains (used in the code above). Zapytania LINQ nie są wykonywane, gdy są one definiowane lub ich modyfikacji przez wywołanie metody, takie jak Where, Contains, lub OrderBy.LINQ queries are not executed when they're defined or when they're modified by calling a method such as Where, Contains, or OrderBy. Przeciwnie wykonanie zapytania jest odroczone.Rather, query execution is deferred. Oznacza to, że wyniku obliczenia wyrażenia zostanie opóźnione, dopóki wartość zrealizowane faktycznie jest powtarzana lub ToListAsync metoda jest wywoływana.That means that the evaluation of an expression is delayed until its realized value is actually iterated over or the ToListAsync method is called. Aby uzyskać więcej informacji na temat wykonywania zapytań z opóźnieniem, zobacz wykonywania zapytania.For more information about deferred query execution, see Query Execution.

Uwaga: Zawiera metoda jest uruchomiona w bazie danych nie jest w kodzie języka c# pokazano powyżej.Note: The Contains method is run on the database, not in the c# code shown above. Rozróżnianie wielkości liter w zapytaniu, zależy od bazy danych i sortowanie.The case sensitivity on the query depends on the database and the collation. W programie SQL Server zawiera mapuje SQL LIKE, która jest uwzględniana wielkość liter.On SQL Server, Contains maps to SQL LIKE, which is case insensitive. W SQLite przy użyciu sortowania domyślnego, jest uwzględniana wielkość liter.In SQLite, with the default collation, it's case sensitive.

Przejdź do adresu /Movies/Index.Navigate to /Movies/Index. Dołącz ciąg zapytania, takie jak ?searchString=Ghost do adresu URL.Append a query string such as ?searchString=Ghost to the URL. Wyświetlane są filtrowane filmów.The filtered movies are displayed.

Widok indeksu

Jeśli zmienisz podpis Index metoda będzie miała parametru o nazwie id, id parametr będzie odpowiadał opcjonalnego {id} symbolu zastępczego dla domyślnej kieruje zestawu w Startup.cs.If you change the signature of the Index method to have a parameter named id, the id parameter will match the optional {id} placeholder for the default routes set in Startup.cs.

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

Zmień parametr id i wszystkie wystąpienia searchString Zmień id.Change the parameter to id and all occurrences of searchString change to id.

Poprzedni Index metody:The previous Index method:

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

Zaktualizowany interfejs Index metody z id parametru:The updated Index method with id parameter:

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

Tytuł wyszukiwania można teraz przekazywać jako dane trasy (segment adresu URL) zamiast jako wartość ciągu zapytania.You can now pass the search title as route data (a URL segment) instead of as a query string value.

Widok indeksu z ghost word dodawany do adresu Url i zwrócony filmu listę filmów Ghostbusters i Ghostbusters 2

Jednak nie można oczekiwać od użytkowników, aby zmodyfikować adres URL, za każdym razem, gdy chcą wyszukiwania filmów.However, you can't expect users to modify the URL every time they want to search for a movie. Teraz należy dodać elementy interfejsu użytkownika, aby pomóc im filtrowanie filmów.So now you'll add UI elements to help them filter movies. Jeśli zmienisz podpis Index metodę, aby przetestować sposób przekazywania trasy powiązanym ID parametr, zmień je z powrotem, aby przyspieszyć parametr o nazwie searchString: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:

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

Otwórz Views/Movies/Index.cshtml pliku i Dodaj <form> znaczników, które przedstawiono poniżej:Open the Views/Movies/Index.cshtml file, and add the <form> markup highlighted below:

    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>

Kod HTML <form> tagów używa Pomocnik tagu formularza, więc gdy prześlesz formularz, aby opublikowaniu ciąg filtru Index akcji kontrolera filmów.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. Zapisz zmiany, a następnie przetestować filtr.Save your changes and then test the filter.

Widok indeksu z ghost słowa wpisane w polu tekstowym filtru tytułu

Istnieje nie [HttpPost] przeciążenia Index metody można by oczekiwać.There's no [HttpPost] overload of the Index method as you might expect. Nie są potrzebne, ponieważ metoda nie jest zmiana stanu aplikacji, po prostu filtrowania danych.You don't need it, because the method isn't changing the state of the app, just filtering data.

Można dodać następujące [HttpPost] Index metody.You could add the following [HttpPost] Index method.

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

notUsed Parametr jest używany do tworzenia przeciążenie dla elementu Index metody.The notUsed parameter is used to create an overload for the Index method. Omówimy, w dalszej części tego samouczka.We'll talk about that later in the tutorial.

Jeśli dodasz tej metody, wywołujący akcji będzie odpowiadać [HttpPost] Index metody i [HttpPost] Index metoda może działać, jak pokazano na poniższej ilustracji.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.

Okno przeglądarki z odpowiedzi aplikacji z indeksu HttpPost: Filtr ghost

Jednak nawet w przypadku dodania to [HttpPost] wersję Index metody, jest to ograniczenie, w jak to wszystko została zaimplementowana.However, even if you add this [HttpPost] version of the Index method, there's a limitation in how this has all been implemented. Wyobraź sobie, że chcesz utworzyć zakładkę określonego wyszukiwania lub chcesz wysłać link do znajomych, mogą kliknąć umożliwiający zobaczenie tej samej listy filtrowane filmów.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. Należy zauważyć, że adres URL żądania HTTP POST jest taki sam jak adres URL dla żądania GET (localhost:xxxxx/filmy/indeksu) — Brak wyszukiwania informacji w adresie URL.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. Informacje o parametrach wyszukiwania są wysyłane do serwera jako stanowią wartość pola.The search string information is sent to the server as a form field value. Możesz sprawdzić, czy za pomocą narzędzia deweloperskie przeglądarki lub doskonałą narzędzie Fiddler.You can verify that with the browser Developer tools or the excellent Fiddler tool. Na poniższym obrazie przedstawiono narzędzia deweloperskie przeglądarki Chrome:The image below shows the Chrome browser Developer tools:

Karta Sieć narzędzi dla deweloperów w programie Microsoft Edge wyświetlanie treść żądania z wartością Ciągwyszukiwania ghost

Możesz zobaczyć parametru wyszukiwania i XSRF tokenu w treści żądania.You can see the search parameter and XSRF token in the request body. Należy pamiętać, zgodnie z opisem w poprzednim samouczku Pomocnik tagu formularza generuje XSRF token zabezpieczający przed sfałszowaniem.Note, as mentioned in the previous tutorial, the Form Tag Helper generates an XSRF anti-forgery token. Dane, nie masz zostać zmodyfikowana, więc nie potrzebujemy przeprowadzić walidacji tokenu w metodzie kontrolera.We're not modifying data, so we don't need to validate the token in the controller method.

Ponieważ parametr wyszukiwania znajduje się w treści żądania, a nie jej adres URL, nie można przechwycić informacje wyszukiwania do zakładki lub udostępnienia innym osobom.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. Poprawka, określając żądania powinna przypominać HTTP GET:Fix this by specifying the request should be HTTP GET:

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

Teraz gdy prześlesz wyszukiwania, adres URL zawiera ciąg zapytania wyszukiwania.Now when you submit a search, the URL contains the search query string. Wyszukiwanie będzie także przejść do HttpGet Index metody akcji, nawet jeśli masz HttpPost Index metody.Searching will also go to the HttpGet Index action method, even if you have a HttpPost Index method.

Okno przeglądarki, przedstawiające Ciągwyszukiwania = ghost w adresie Url i filmów, zwrócone, Ghostbusters i Ghostbusters 2, zawierają ghost programu word

Następujący kod przedstawia zmiany form tag:The following markup shows the change to the form tag:

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

Dodaj wyszukiwanie według gatunkuAdd Search by genre

Dodaj następujący kod MovieGenreViewModel klasy modeli folderu:Add the following MovieGenreViewModel class to the Models folder:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace MvcMovie.Models
{
    public class MovieGenreViewModel
    {
        public List<Movie> Movies { get; set; }
        public SelectList Genres { get; set; }
        public string MovieGenre { get; set; }
        public string SearchString { get; set; }
    }
}

Model widoku gatunku filmu będzie zawierać:The movie-genre view model will contain:

  • Lista filmów.A list of movies.
  • A SelectList zawierającego listę gatunki.A SelectList containing the list of genres. Dzięki temu użytkownikowi na wybranie określonego rodzaju z listy.This allows the user to select a genre from the list.
  • MovieGenre, zawierającą wybrane gatunku.MovieGenre, which contains the selected genre.
  • SearchString, który zawiera tekst, użytkownicy wprowadzają w polu tekstowym wyszukiwania.SearchString, which contains the text users enter in the search text box.

Zastąp Index method in Class metoda MoviesController.cs następującym kodem:Replace the Index method in MoviesController.cs with the following code:

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

Poniższy kod jest LINQ zapytania, który pobiera wszystkie gatunki z bazy danych.The following code is a LINQ query that retrieves all the genres from the database.

// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
                                orderby m.Genre
                                select m.Genre;

SelectList Gatunków jest tworzona przy wyświetlaniu distinct gatunki (nie chcemy naszej listy wyboru mają zduplikowane gatunki).The SelectList of genres is created by projecting the distinct genres (we don't want our select list to have duplicate genres).

Gdy użytkownik wyszukuje element, do wartości wyszukiwania są przechowywane w polu wyszukiwania.When the user searches for the item, the search value is retained in the search box.

Dodaj wyszukiwanie według gatunku do widoku indeksuAdd search by genre to the Index view

Aktualizacja Index.cshtml w następujący sposób:Update Index.cshtml as follows:

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

Sprawdź wyrażenie lambda, używane w następujących pomocnika kodu HTML:Examine the lambda expression used in the following HTML Helper:

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

W poprzednim kodzie DisplayNameFor sprawdza pomocnika kodu HTML Title właściwość, do którego odwołuje się wyrażenie lambda, aby ustalić nazwę wyświetlaną.In the preceding code, the DisplayNameFor HTML Helper inspects the Title property referenced in the lambda expression to determine the display name. Ponieważ wyrażenie lambda jest kontrolowane zamiast oceniane, nie otrzymasz naruszenie zasad dostępu podczas model, model.Movies, lub model.Movies[0]null lub jest pusty.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. Kiedy jest obliczane wyrażenie lambda (na przykład @Html.DisplayFor(modelItem => item.Title)), są oceniane wartości właściwości modelu.When the lambda expression is evaluated (for example, @Html.DisplayFor(modelItem => item.Title)), the model's property values are evaluated.

Testowanie aplikacji przez wyszukiwanie według gatunku, tytuł filmu i przez:Test the app by searching by genre, by movie title, and by both:

Wyniki przedstawiający okno przeglądarki https://localhost:5001/Movies?MovieGenre=Comedy&SearchString=2