次の方法で共有


パート 6、ASP.NET Core Razor ページへの検索の追加

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。

作成者: Rick Anderson

次のセクションでは、ジャンルまたは名前による映画検索が追加されます。

次の強調表示されたコードを Pages/Movies/Index.cshtml.cs に追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    [BindProperty(SupportsGet = true)]
    public string? SearchString { get; set; }

    public SelectList? Genres { get; set; }

    [BindProperty(SupportsGet = true)]
    public string? MovieGenre { get; set; }

上のコードでは、次のようになります。

  • SearchString: ユーザーが検索テキスト ボックスに入力したテキストが含まれる。 SearchString には、[BindProperty] 属性があります。 [BindProperty] により、プロパティと同じ名前に基づきフォーム値とクエリ文字列がバインドされます。 HTTP GET 要求でのバインドには [BindProperty(SupportsGet = true)] が必要です。
  • Genres: ジャンル一覧が含まれる。 Genres により、ユーザーは一覧からジャンルを選択できます。 SelectList には using Microsoft.AspNetCore.Mvc.Rendering; が必要です。
  • MovieGenre: ユーザーが選択する特定のジャンルが含まれる。 たとえば、"Western (西部劇)" です。
  • GenresMovieGenre は、このチュートリアルで後述します。

警告

セキュリティ上の理由から、ページ モデルのプロパティに対して GET 要求データのバインドをオプトインする必要があります。 プロパティにマップする前に、ユーザー入力を確認してください。 GET バインドをオプトインするのは、クエリ文字列やルート値に依存するシナリオに対処する場合に便利です。

GET 要求のプロパティをバインドするには、[BindProperty] 属性の SupportsGet プロパティを true に設定します。

[BindProperty(SupportsGet = true)]

詳細については、「ASP.NET Core コミュニティ スタンドアップ: GET ディスカッション (YouTube) でのバインド」を参照してください。

Movies/Index ページの OnGetAsync メソッドを次のコードで更新します。

public async Task OnGetAsync()
{
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
    {
        movies = movies.Where(s => s.Title.Contains(SearchString));
    }

    Movie = await movies.ToListAsync();
}

OnGetAsync メソッドの最初の行により、ムービーを選択する LINQ クエリが作成されます。

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

このクエリは、この時点では "定義されている" だけであり、データベースに対して実行されているわけでは "ありません"。

SearchString プロパティが null でも空でもない場合、検索文字列で絞り込むようにムービークエリが変更されます。

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

s => s.Title.Contains() コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、Where メソッドや Contains など、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、WhereContainsOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリ実行は先送りされます。 その具体値が繰り返されるか、ToListAsync メソッドが呼び出されるまで、式の評価が延ばされます。 詳細については、「クエリ実行」を参照してください。

Note

Contains メソッドは C# コードではなく、データベースで実行されます。 クエリの大文字と小文字の区別は、データベースや照合順序に依存します。 SQL Server では、Contains は大文字/小文字の区別がない SQL LIKE にマッピングされます。 既定の照合順序での SQLite は、クエリに応じて、大文字と小文字を区別する場合と区別 "しない" 場合が混在します。 大文字と小文字を区別しない SQLite クエリの作成については、次を参照してください。

ムービー ページに移動し、?searchString=Ghost のようなクエリ文字列を URL に追加します。 たとえば、「 https://localhost:5001/Movies?searchString=Ghost 」のように入力します。 フィルターされたムービーが表示されます。

インデックス ビュー

次のルート テンプレートが Index ページに追加される場合、検索文字列を URL セグメントとして渡すことができます。 たとえば、「 https://localhost:5001/Movies/Ghost 」のように入力します。

@page "{searchString?}"

先のルート制約では、クエリ文字列値の代わりに、ルート データ (URL セグメント) として題名を検索できます。 ?"{searchString?}" は、これが任意のルート パラメーターであることを意味します。

ghost という単語が URL に追加された索引ビュー。Ghostbusters と Ghostbusters 2 という 2 本のムービーからなるムービーリストが返されています。

ASP.NET Core ランタイムではモデル バインドを使用し、クエリ文字列 (SearchString) またはルート データ (?searchString=Ghost) から https://localhost:5001/Movies/Ghost プロパティの値が設定されます。 モデル バインドでは、大文字と小文字は区別 "されません"。

ただし、URL を変更してムービーを検索することをユーザーに求めることはできません。 この手順では、ムービーを絞り込むための UI を追加します。 ルート制約 "{searchString?}" を追加した場合、それを削除します。

Pages/Movies/Index.cshtml ファイルを開き、次のコードで強調表示されているマークアップを追加します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    <thead>

HTML <form> タグでは、次のタグ ヘルパーが使用されます。

変更を保存し、フィルターをテストします。

タイトル フィルター テキストボックスに ghost という単語が入力されたインデックス ビュー

ジャンルで検索する

Movies/Index.cshtml.cs ページの OnGetAsync メソッドを次のコードで更新します。

public async Task OnGetAsync()
{
    // <snippet_search_linqQuery>
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    // </snippet_search_linqQuery>

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

    // <snippet_search_selectList>
    Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
    // </snippet_search_selectList>
    Movie = await movies.ToListAsync();
}

次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。

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

一意のジャンルを投影することによってジャンルの SelectList を作成します。

Genres = new SelectList(await genreQuery.Distinct().ToListAsync());

ジャンル検索を Razor ページに追加する

Index.cshtml<form> 要素を、次のマークアップで強調されている部分のとおりに更新します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

ジャンルまたはムービーのタイトル、あるいはその両方で検索して、アプリをテストします。

ジャンル セレクターとタイトル テキスト ボックスの検索フィルターが追加されて完成したインデックス ビュー

次のステップ

次のセクションでは、ジャンルまたは名前による映画検索が追加されます。

次の強調表示されたコードを Pages/Movies/Index.cshtml.cs に追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    [BindProperty(SupportsGet = true)]
    public string? SearchString { get; set; }

    public SelectList? Genres { get; set; }

    [BindProperty(SupportsGet = true)]
    public string? MovieGenre { get; set; }

上のコードでは、次のようになります。

  • SearchString: ユーザーが検索テキスト ボックスに入力したテキストが含まれる。 SearchString には、[BindProperty] 属性があります。 [BindProperty] により、プロパティと同じ名前に基づきフォーム値とクエリ文字列がバインドされます。 HTTP GET 要求でのバインドには [BindProperty(SupportsGet = true)] が必要です。
  • Genres: ジャンル一覧が含まれる。 Genres により、ユーザーは一覧からジャンルを選択できます。 SelectList には using Microsoft.AspNetCore.Mvc.Rendering; が必要です。
  • MovieGenre: ユーザーが選択する特定のジャンルが含まれる。 たとえば、"Western (西部劇)" です。
  • GenresMovieGenre は、このチュートリアルで後述します。

警告

セキュリティ上の理由から、ページ モデルのプロパティに対して GET 要求データのバインドをオプトインする必要があります。 プロパティにマップする前に、ユーザー入力を確認してください。 GET バインドをオプトインするのは、クエリ文字列やルート値に依存するシナリオに対処する場合に便利です。

GET 要求のプロパティをバインドするには、[BindProperty] 属性の SupportsGet プロパティを true に設定します。

[BindProperty(SupportsGet = true)]

詳細については、「ASP.NET Core コミュニティ スタンドアップ: GET ディスカッション (YouTube) でのバインド」を参照してください。

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
    {
        movies = movies.Where(s => s.Title.Contains(SearchString));
    }

    Movie = await movies.ToListAsync();
}

OnGetAsync メソッドの最初の行により、ムービーを選択する LINQ クエリが作成されます。

// using System.Linq;
var movies = from m in _context.Movie
             select m;

このクエリは、この時点では "定義されている" だけであり、データベースに対して実行されているわけでは "ありません"。

SearchString プロパティが null でも空でもない場合、検索文字列で絞り込むようにムービークエリが変更されます。

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

s => s.Title.Contains() コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、Where メソッドや Contains など、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、WhereContainsOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリ実行は先送りされます。 その具体値が繰り返されるか、ToListAsync メソッドが呼び出されるまで、式の評価が延ばされます。 詳細については、「クエリ実行」を参照してください。

Note

Contains メソッドは C# コードではなく、データベースで実行されます。 クエリの大文字と小文字の区別は、データベースや照合順序に依存します。 SQL Server では、Contains は大文字/小文字の区別がない SQL LIKE にマッピングされます。 既定の照合順序での SQLite は、クエリに応じて、大文字と小文字を区別する場合と区別 "しない" 場合が混在します。 大文字と小文字を区別しない SQLite クエリの作成については、次を参照してください。

ムービー ページに移動し、?searchString=Ghost のようなクエリ文字列を URL に追加します。 たとえば、「 https://localhost:5001/Movies?searchString=Ghost 」のように入力します。 フィルターされたムービーが表示されます。

インデックス ビュー

次のルート テンプレートが Index ページに追加される場合、検索文字列を URL セグメントとして渡すことができます。 たとえば、「 https://localhost:5001/Movies/Ghost 」のように入力します。

@page "{searchString?}"

先のルート制約では、クエリ文字列値の代わりに、ルート データ (URL セグメント) として題名を検索できます。 ?"{searchString?}" は、これが任意のルート パラメーターであることを意味します。

ghost という単語が URL に追加された索引ビュー。Ghostbusters と Ghostbusters 2 という 2 本のムービーからなるムービーリストが返されています。

ASP.NET Core ランタイムではモデル バインドを使用し、クエリ文字列 (SearchString) またはルート データ (?searchString=Ghost) から https://localhost:5001/Movies/Ghost プロパティの値が設定されます。 モデル バインドでは、大文字と小文字は区別 "されません"。

ただし、URL を変更してムービーを検索することをユーザーに求めることはできません。 この手順では、ムービーを絞り込むための UI を追加します。 ルート制約 "{searchString?}" を追加した場合、それを削除します。

Pages/Movies/Index.cshtml ファイルを開き、次のコードで強調表示されているマークアップを追加します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    @*Markup removed for brevity.*@

HTML <form> タグでは、次のタグ ヘルパーが使用されます。

変更を保存し、フィルターをテストします。

タイトル フィルター テキストボックスに ghost という単語が入力されたインデックス ビュー

ジャンルで検索する

Movies/Index.cshtml.cs ページの OnGetAsync メソッドを次のコードで更新します。

public async Task OnGetAsync()
{
    // 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);
    }
    Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
    Movie = await movies.ToListAsync();
}

次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。

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

ジャンルの SelectList は、別個のジャンルを推定することで作成されます。

Genres = new SelectList(await genreQuery.Distinct().ToListAsync());

ジャンル検索を Razor ページに追加する

Index.cshtml<form> 要素を、次のマークアップで強調されている部分のとおりに更新します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

ジャンルまたはムービーのタイトル、あるいはその両方で検索して、アプリをテストします。

次のステップ

次のセクションでは、ジャンルまたは名前による映画検索が追加されます。

次の強調表示されたコードを Pages/Movies/Index.cshtml.cs に追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    [BindProperty(SupportsGet = true)]
    public string? SearchString { get; set; }

    public SelectList? Genres { get; set; }

    [BindProperty(SupportsGet = true)]
    public string? MovieGenre { get; set; }

上のコードでは、次のようになります。

  • SearchString: ユーザーが検索テキスト ボックスに入力したテキストが含まれる。 SearchString には、[BindProperty] 属性があります。 [BindProperty] により、プロパティと同じ名前に基づきフォーム値とクエリ文字列がバインドされます。 HTTP GET 要求でのバインドには [BindProperty(SupportsGet = true)] が必要です。
  • Genres: ジャンル一覧が含まれる。 Genres により、ユーザーは一覧からジャンルを選択できます。 SelectList には using Microsoft.AspNetCore.Mvc.Rendering; が必要です。
  • MovieGenre: ユーザーが選択する特定のジャンルが含まれる。 たとえば、"Western (西部劇)" です。
  • GenresMovieGenre は、このチュートリアルで後述します。

警告

セキュリティ上の理由から、ページ モデルのプロパティに対して GET 要求データのバインドをオプトインする必要があります。 プロパティにマップする前に、ユーザー入力を確認してください。 GET バインドをオプトインするのは、クエリ文字列やルート値に依存するシナリオに対処する場合に便利です。

GET 要求のプロパティをバインドするには、[BindProperty] 属性の SupportsGet プロパティを true に設定します。

[BindProperty(SupportsGet = true)]

詳細については、「ASP.NET Core コミュニティ スタンドアップ: GET ディスカッション (YouTube) でのバインド」を参照してください。

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
    {
        movies = movies.Where(s => s.Title.Contains(SearchString));
    }

    Movie = await movies.ToListAsync();
}

OnGetAsync メソッドの最初の行により、ムービーを選択する LINQ クエリが作成されます。

// using System.Linq;
var movies = from m in _context.Movie
             select m;

このクエリは、この時点では "定義されている" だけであり、データベースに対して実行されているわけでは "ありません"。

SearchString プロパティが null でも空でもない場合、検索文字列で絞り込むようにムービークエリが変更されます。

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

s => s.Title.Contains() コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、Where メソッドや Contains など、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、WhereContainsOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリ実行は先送りされます。 その具体値が繰り返されるか、ToListAsync メソッドが呼び出されるまで、式の評価が延ばされます。 詳細については、「クエリ実行」を参照してください。

Note

Contains メソッドは C# コードではなく、データベースで実行されます。 クエリの大文字と小文字の区別は、データベースや照合順序に依存します。 SQL Server では、Contains は大文字/小文字の区別がない SQL LIKE にマッピングされます。 既定の照合順序での SQLite は、クエリに応じて、大文字と小文字を区別する場合と区別 "しない" 場合が混在します。 大文字と小文字を区別しない SQLite クエリの作成については、次を参照してください。

ムービー ページに移動し、?searchString=Ghost のようなクエリ文字列を URL に追加します。 たとえば、「 https://localhost:5001/Movies?searchString=Ghost 」のように入力します。 フィルターされたムービーが表示されます。

インデックス ビュー

次のルート テンプレートが Index ページに追加される場合、検索文字列を URL セグメントとして渡すことができます。 たとえば、「 https://localhost:5001/Movies/Ghost 」のように入力します。

@page "{searchString?}"

先のルート制約では、クエリ文字列値の代わりに、ルート データ (URL セグメント) として題名を検索できます。 ?"{searchString?}" は、これが任意のルート パラメーターであることを意味します。

ghost という単語が URL に追加された索引ビュー。Ghostbusters と Ghostbusters 2 という 2 本のムービーからなるムービーリストが返されています。

ASP.NET Core ランタイムではモデル バインドを使用し、クエリ文字列 (SearchString) またはルート データ (?searchString=Ghost) から https://localhost:5001/Movies/Ghost プロパティの値が設定されます。 モデル バインドでは、大文字と小文字は区別 "されません"。

ただし、URL を変更してムービーを検索することをユーザーに求めることはできません。 この手順では、ムービーを絞り込むための UI を追加します。 ルート制約 "{searchString?}" を追加した場合、それを削除します。

Pages/Movies/Index.cshtml ファイルを開き、次のコードで強調表示されているマークアップを追加します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    @*Markup removed for brevity.*@

HTML <form> タグでは、次のタグ ヘルパーが使用されます。

変更を保存し、フィルターをテストします。

タイトル フィルター テキストボックスに ghost という単語が入力されたインデックス ビュー

ジャンルで検索する

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    // 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);
    }
    Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
    Movie = await movies.ToListAsync();
}

次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。

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

ジャンルの SelectList は、別個のジャンルを推定することで作成されます。

Genres = new SelectList(await genreQuery.Distinct().ToListAsync());

ジャンル検索を Razor ページに追加する

Index.cshtml<form> 要素を、次のマークアップで強調されている部分のとおりに更新します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

ジャンルまたはムービーのタイトル、あるいはその両方で検索して、アプリをテストします。

次のステップ

次のセクションでは、ジャンルまたは名前による映画検索が追加されます。

次の強調表示されたコードを Pages/Movies/Index.cshtml.cs に追加します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;
        [BindProperty(SupportsGet = true)]
        public string ? SearchString { get; set; }
        public SelectList ? Genres { get; set; }
        [BindProperty(SupportsGet = true)]
        public string ? MovieGenre { get; set; }

上のコードでは、次のようになります。

  • SearchString: ユーザーが検索テキスト ボックスに入力したテキストが含まれる。 SearchString には、[BindProperty] 属性があります。 [BindProperty] により、プロパティと同じ名前に基づきフォーム値とクエリ文字列がバインドされます。 HTTP GET 要求でのバインドには [BindProperty(SupportsGet = true)] が必要です。
  • Genres: ジャンル一覧が含まれる。 Genres により、ユーザーは一覧からジャンルを選択できます。 SelectList には using Microsoft.AspNetCore.Mvc.Rendering; が必要です。
  • MovieGenre: ユーザーが選択する特定のジャンルが含まれる。 たとえば、"Western (西部劇)" です。
  • GenresMovieGenre は、このチュートリアルで後述します。

警告

セキュリティ上の理由から、ページ モデルのプロパティに対して GET 要求データのバインドをオプトインする必要があります。 プロパティにマップする前に、ユーザー入力を確認してください。 GET バインドをオプトインするのは、クエリ文字列やルート値に依存するシナリオに対処する場合に便利です。

GET 要求のプロパティをバインドするには、[BindProperty] 属性の SupportsGet プロパティを true に設定します。

[BindProperty(SupportsGet = true)]

詳細については、「ASP.NET Core コミュニティ スタンドアップ: GET ディスカッション (YouTube) でのバインド」を参照してください。

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
    {
        movies = movies.Where(s => s.Title.Contains(SearchString));
    }

    Movie = await movies.ToListAsync();
}

OnGetAsync メソッドの最初の行により、ムービーを選択する LINQ クエリが作成されます。

// using System.Linq;
var movies = from m in _context.Movie
             select m;

このクエリは、この時点では "定義されている" だけであり、データベースに対して実行されているわけでは "ありません"。

SearchString プロパティが null でも空でもない場合、検索文字列で絞り込むようにムービークエリが変更されます。

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

s => s.Title.Contains() コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、Where メソッドや Contains など、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、WhereContainsOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリ実行は先送りされます。 その具体値が繰り返されるか、ToListAsync メソッドが呼び出されるまで、式の評価が延ばされます。 詳細については、「クエリ実行」を参照してください。

Note

Contains メソッドは C# コードではなく、データベースで実行されます。 クエリの大文字と小文字の区別は、データベースや照合順序に依存します。 SQL Server では、Contains は大文字/小文字の区別がない SQL LIKE にマッピングされます。 既定の照合順序での SQLite は、クエリに応じて、大文字と小文字を区別する場合と区別 "しない" 場合が混在します。 大文字と小文字を区別しない SQLite クエリの作成については、次を参照してください。

ムービー ページに移動し、?searchString=Ghost のようなクエリ文字列を URL に追加します。 たとえば、「 https://localhost:5001/Movies?searchString=Ghost 」のように入力します。 フィルターされたムービーが表示されます。

インデックス ビュー

次のルート テンプレートが Index ページに追加される場合、検索文字列を URL セグメントとして渡すことができます。 たとえば、「 https://localhost:5001/Movies/Ghost 」のように入力します。

@page "{searchString?}"

先のルート制約では、クエリ文字列値の代わりに、ルート データ (URL セグメント) として題名を検索できます。 ?"{searchString?}" は、これが任意のルート パラメーターであることを意味します。

ghost という単語が URL に追加された索引ビュー。Ghostbusters と Ghostbusters 2 という 2 本のムービーからなるムービーリストが返されています。

ASP.NET Core ランタイムではモデル バインドを使用し、クエリ文字列 (SearchString) またはルート データ (?searchString=Ghost) から https://localhost:5001/Movies/Ghost プロパティの値が設定されます。 モデル バインドでは、大文字と小文字は区別 "されません"。

ただし、URL を変更してムービーを検索することをユーザーに求めることはできません。 この手順では、ムービーを絞り込むための UI を追加します。 ルート制約 "{searchString?}" を追加した場合、それを削除します。

Pages/Movies/Index.cshtml ファイルを開き、次のコードで強調表示されているマークアップを追加します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    @*Markup removed for brevity.*@

HTML <form> タグでは、次のタグ ヘルパーが使用されます。

変更を保存し、フィルターをテストします。

タイトル フィルター テキストボックスに ghost という単語が入力されたインデックス ビュー

ジャンルで検索する

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    // 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);
    }
    Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
    Movie = await movies.ToListAsync();
}

次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。

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

ジャンルの SelectList は、別個のジャンルを推定することで作成されます。

Genres = new SelectList(await genreQuery.Distinct().ToListAsync());

ジャンル検索を Razor ページに追加する

Index.cshtml<form> 要素を、次のマークアップで強調されている部分のとおりに更新します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

ジャンルまたはムービーのタイトル、あるいはその両方で検索して、アプリをテストします。

次のステップ

次のセクションでは、ジャンルまたは名前による映画検索が追加されます。

強調表示されている次の using ステートメントとプロパティを Pages/Movies/Index.cshtml.cs に追加します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{

    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get; set; }
        [BindProperty(SupportsGet = true)]
        public string SearchString { get; set; }
        public SelectList Genres { get; set; }
        [BindProperty(SupportsGet = true)]
        public string MovieGenre { get; set; }

上のコードでは、次のようになります。

  • SearchString: ユーザーが検索テキスト ボックスに入力したテキストが含まれる。 SearchString には、[BindProperty] 属性があります。 [BindProperty] により、プロパティと同じ名前に基づきフォーム値とクエリ文字列がバインドされます。 HTTP GET 要求でのバインドには [BindProperty(SupportsGet = true)] が必要です。
  • Genres: ジャンル一覧が含まれる。 Genres により、ユーザーは一覧からジャンルを選択できます。 SelectList には using Microsoft.AspNetCore.Mvc.Rendering; が必要です。
  • MovieGenre: ユーザーが選択する特定のジャンルが含まれる。 たとえば、"Western (西部劇)" です。
  • GenresMovieGenre は、このチュートリアルで後述します。

警告

セキュリティ上の理由から、ページ モデルのプロパティに対して GET 要求データのバインドをオプトインする必要があります。 プロパティにマップする前に、ユーザー入力を確認してください。 GET バインドをオプトインするのは、クエリ文字列やルート値に依存するシナリオに対処する場合に便利です。

GET 要求のプロパティをバインドするには、[BindProperty] 属性の SupportsGet プロパティを true に設定します。

[BindProperty(SupportsGet = true)]

詳細については、「ASP.NET Core コミュニティ スタンドアップ: GET ディスカッション (YouTube) でのバインド」を参照してください。

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
    {
        movies = movies.Where(s => s.Title.Contains(SearchString));
    }

    Movie = await movies.ToListAsync();
}

OnGetAsync メソッドの最初の行により、ムービーを選択する LINQ クエリが作成されます。

// using System.Linq;
var movies = from m in _context.Movie
             select m;

このクエリは、この時点では "定義されている" だけであり、データベースに対して実行されているわけでは "ありません"。

SearchString プロパティが null でも空でもない場合、検索文字列で絞り込むようにムービークエリが変更されます。

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

s => s.Title.Contains() コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、Where メソッドや Contains など、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、WhereContainsOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリ実行は先送りされます。 その具体値が繰り返されるか、ToListAsync メソッドが呼び出されるまで、式の評価が延ばされます。 詳細については、「クエリ実行」を参照してください。

Note

Contains メソッドは C# コードではなく、データベースで実行されます。 クエリの大文字と小文字の区別は、データベースや照合順序に依存します。 SQL Server では、Contains は大文字/小文字の区別がない SQL LIKE にマッピングされます。 既定の照合順序での SQLite は、クエリに応じて、大文字と小文字を区別する場合と区別 "しない" 場合が混在します。 大文字と小文字を区別しない SQLite クエリの作成については、次を参照してください。

ムービー ページに移動し、?searchString=Ghost のようなクエリ文字列を URL に追加します。 たとえば、「 https://localhost:5001/Movies?searchString=Ghost 」のように入力します。 フィルターされたムービーが表示されます。

インデックス ビュー

次のルート テンプレートが Index ページに追加される場合、検索文字列を URL セグメントとして渡すことができます。 たとえば、「 https://localhost:5001/Movies/Ghost 」のように入力します。

@page "{searchString?}"

先のルート制約では、クエリ文字列値の代わりに、ルート データ (URL セグメント) として題名を検索できます。 ?"{searchString?}" は、これが任意のルート パラメーターであることを意味します。

ghost という単語が URL に追加された索引ビュー。Ghostbusters と Ghostbusters 2 という 2 本のムービーからなるムービーリストが返されています。

ASP.NET Core ランタイムではモデル バインドを使用し、クエリ文字列 (SearchString) またはルート データ (?searchString=Ghost) から https://localhost:5001/Movies/Ghost プロパティの値が設定されます。 モデル バインドでは、大文字と小文字は区別 "されません"。

ただし、URL を変更してムービーを検索することをユーザーに求めることはできません。 この手順では、ムービーを絞り込むための UI を追加します。 ルート制約 "{searchString?}" を追加した場合、それを削除します。

Pages/Movies/Index.cshtml ファイルを開き、次のコードで強調表示されているマークアップを追加します。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>

<form>
    <p>
        <label>Title: <input type="text" asp-for="SearchString" /></label>
        <input type="submit" value="Filter" />
    </p>
</form>

<table class="table">
    @*Markup removed for brevity.*@

HTML <form> タグでは、次のタグ ヘルパーが使用されます。

変更を保存し、フィルターをテストします。

タイトル フィルター テキストボックスに ghost という単語が入力されたインデックス ビュー

ジャンルで検索する

索引ページの OnGetAsync メソッドを次のコードに変更します。

public async Task OnGetAsync()
{
    // 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);
    }
    Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
    Movie = await movies.ToListAsync();
}

次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。

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

ジャンルの SelectList は、別個のジャンルを推定することで作成されます。

Genres = new SelectList(await genreQuery.Distinct().ToListAsync());

ジャンル検索を Razor ページに追加する

  1. Index.cshtml<form> 要素を、次のマークアップで強調されている部分のとおりに更新します。

    @page
    @model RazorPagesMovie.Pages.Movies.IndexModel
    
    @{
        ViewData["Title"] = "Index";
    }
    
    <h1>Index</h1>
    
    <p>
        <a asp-page="Create">Create New</a>
    </p>
    
    <form>
        <p>
            <select asp-for="MovieGenre" asp-items="Model.Genres">
                <option value="">All</option>
            </select>
            <label>Title: <input type="text" asp-for="SearchString" /></label>
            <input type="submit" value="Filter" />
        </p>
    </form>
    
    <table class="table">
        @*Markup removed for brevity.*@
    
    
  2. ジャンルまたはムービーのタイトル、あるいはその両方で検索して、アプリをテストします。

次のステップ