検索する
Note
Visual Studio の最新バージョンを使用しているこのチュートリアルの更新版は、こちらからアクセスできます。 新しいチュートリアルでは ASP.NET Core MVC を使用しており、このチュートリアルよりも多くの改良が加えられています。
このチュートリアルでは、ASP.NET Core MVC のコントローラーとビューについて説明します。 Razor Pages は ASP.NET Core の新しい代替手段であり、Web UI の構築を容易にし、生産性を高めるページベースのプログラミング モデルです。 MVC のバージョンの前に、Razor ページのチュートリアルを試すことをお勧めします。 この Razor ページのチュートリアルの特徴は次のとおりです。
- 使いやすい。
- 多くの機能をカバーしている。
- 新しいアプリ開発には、これが最適なアプローチです。
検索メソッドと検索ビューを追加する
このセクションでは、検索機能を Index
アクション メソッドに追加して、ジャンルまたは名前でムービーを検索できるようにします。
前提条件
このセクションのスクリーンショットと一致するには、アプリケーション (F5) を実行し、次のムービーをデータベースに追加する必要があります。
Title | リリース日 | Genre | 価格 |
---|---|---|---|
ゴーストバスターズ | 1984/6/8 | コメディ | 6.99 |
ゴーストバスターズ | 1989/6/16 | コメディ | 6.99 |
猿の惑星 | 1986/3/27 | アクション | 5.99 |
インデックス フォームの更新
まず、既存の MoviesController
クラスに対して Index
アクション メソッドを更新します。 のコードを次に示します。
public ActionResult Index(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Index
メソッド内の 1 行目では、次のように、映画を選択するための LINQ クエリを作成しています。
var movies = from m in db.Movies
select m;
この時点でクエリが定義されますが、まだデータベースに対して実行はされません。
searchString
パラメーターに文字列が含まれる場合、以下のコードを使用して、検索文字列の値でフィルターするようにムービー クエリが変更されます。
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
上の s => s.Title
コードはラムダ式です。 ラムダは、メソッド ベースの LINQ クエリで、上のコードで使用されている Where メソッドなど、標準クエリ演算子メソッドの引数として使用されます。 LINQ クエリは、Where
や OrderBy
などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリの実行が先延ばしされるため、式の評価は、実現された値が実際に反復評価されるか ToList
メソッドが呼び出される時点まで遅延されます。 Search
サンプルの場合、クエリは Index.cshtml ビューで実行されます。 クエリの遅延実行の詳細については、「クエリの実行」を参照してください。
Note
Contains メソッドは上記の C# コードではなく、データベースで実行されます。 データベースでは、Contains は大文字と小文字の区別がない SQL LIKE にマッピングされます。
以上で、フォームをユーザーに表示する Index
ビューを更新できるようになりました。
アプリケーションを実行し、/Movies/Index に移動します。 ?searchString=ghost
などのクエリ文字列を URL に追加します。 フィルターされたムービーが表示されます。
id
という名前のパラメーターを使用するために Index
メソッドのシグネチャを変更すると、id
パラメーターは、App_Start\RouteConfig.cs ファイルで設定されている既定ルートの {id}
プレースホルダーと一致するようになります。
{controller}/{action}/{id}
元の Index
メソッドは以下のとおりです。
public ActionResult Index(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
変更後の Index
メソッドは次のようになります。
public ActionResult Index(string id)
{
string searchString = id;
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
これで、クエリ文字列の値ではなく、ルート データ (URL セグメント) として検索タイトルを渡すことができます。
ただし、ユーザーがムービーを検索するたびに URL の変更を求めることはできません。 そのため、ここでは UI を追加して、ムービーをフィルターできるようにします。 ルート バインドされた ID パラメーターを渡す方法をテストするために Index
メソッドのシグネチャを変更した場合は、Index
メソッドが searchString
という名前の文字列パラメーターを受け取るようにシグネチャを元に戻します。
public ActionResult Index(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Views\Movies\Index.cshtml ファイルを開き、@Html.ActionLink("Create New", "Create")
の直後に以下の強調表示されているマークアップを追加します。
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
</p>
Html.BeginForm
ヘルパーは、開始 <form>
タグを作成します。 [Filter (フィルター)] ボタンのクリック操作でフォームが送信されると、Html.BeginForm
ヘルパーにより、フォームがそれ自体に送信されます。
Visual Studio 2013 では、ビュー ファイルの表示と編集が改善されました。 ビュー ファイルを開いた状態でアプリケーションを実行すると、Visual Studio 2013 が正しいコントローラー アクション メソッドを呼び出し、ビューが表示されます。
Visual Studio で (上の図に示すように) インデックス ビューを開いた状態で、Ctr F5 または F5 をタップしてアプリケーションを実行し、ムービーを検索してみてください。
Index
メソッドの HttpPost
オーバーロードはありません。 このメソッドはデータをフィルター処理するだけで、アプリケーションの状態を変更しないため、必要がないからです。
以下の HttpPost Index
メソッドを追加できます。 この場合、アクション呼び出し元は HttpPost Index
メソッドと一致し、HttpPost Index
メソッドが下の図のように実行されます。
[HttpPost]
public string Index(FormCollection fc, string searchString)
{
return "<h3> From [HttpPost]Index: " + searchString + "</h3>";
}
ただし、この HttpPost
バージョンの Index
メソッドを追加しても、実装方法は制限されます。 たとえば、特定の検索をブックマークするか、友だちにリンクを送信し、友だちがそれをクリックしてムービーのフィルターされた同じリストを表示できるようにするとします。 HTTP POST 要求の URL は、GET 要求の URL (localhost:xxxxx/Movies/Index) と同じであり、URL 自体には検索情報がないことに注意してください。 ここでは、検索文字列情報はフォーム フィールドの値の 1 つとしてサーバーに送信されます。 つまり、その検索情報をキャプチャしてブックマーク登録したり、URL で友人に送信したりすることはできません。
解決するには、BeginForm
のオーバーロードを使用して、POST 要求で検索情報を URL に追加して Index
メソッドの HttpGet
バージョンにルーティングする必要があることを指定します。 既存のパラメーターなしの BeginForm
メソッドを以下のマークアップに置き換えます。
@using (Html.BeginForm("Index","Movies",FormMethod.Get))
ここで検索を送信すると、URL に検索クエリ文字列が含められます。 HttpPost Index
メソッドがある場合でも、検索時には HttpGet Index
アクション メソッドにも移動します。
ジャンル別検索を追加する
先ほど Index
メソッドの HttpPost
バージョンを追加した場合は、ここで削除してください。
次は、映画のジャンル別検索をユーザーに提供する機能を追加します。 Index
メソッドを次のコードで置き換えます。
public ActionResult Index(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
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);
}
return View(movies);
}
Index
メソッドのこのバージョンは、movieGenre
という名前の追加のパラメーターを受け取ります。 このコードの先頭部分では、データベースから映画のジャンル情報を取得して保持するための List
オブジェクトを作成しています。
次のコードは、データベースからすべてのジャンルを取得する LINQ クエリです。
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
このコードでは、ジェネリックな List
コレクションの AddRange
メソッドを使用して、異なる映画ジャンル名をすべてリストに追加します (もし Distinct
修飾子を指定しないと、同じジャンルが複数回出現することになります。たとえば、このサンプルではコメディが 2 つになります)。 その後、ジャンルのリストを ViewBag.MovieGenre
オブジェクトに格納します。 カテゴリ データ (映画ジャンルなど) を SelectList オブジェクトとして ViewBag
に格納し、ドロップダウン リスト ボックスでカテゴリ データにアクセスすることは、MVC アプリケーションの一般的なアプローチです。
以下のコードは、movieGenre
パラメーターを検査する方法を示しています。 このコードは、このパラメーターが空でない場合にはムービー クエリの制約を厳しくし、指定したジャンルのムービーだけが選択されるように制限されるようにします。
if (!string.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
}
前述のように、ムービー リストが反復処理されるまで (Index
アクション メソッドが返った後にビューで実行されます)、クエリはデータベースで実行されません。
インデックス ビューにマークアップを追加してジャンル別の検索をサポートする
Views\Movies\Index.cshtml ファイル内の TextBox
ヘルパーの直前に Html.DropDownList
ヘルパーを追加します。 完成したマークアップを以下に示します。
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm("Index", "Movies", FormMethod.Get))
{
<p>
Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p>
}
</p>
<table class="table">
次のコードの内容は以下のとおりです。
@Html.DropDownList("movieGenre", "All")
パラメーター "MovieGenre" は、DropDownList
ヘルパーが ViewBag
で IEnumerable<SelectListItem>
を検索するキーを提供します。 ViewBag
がアクション メソッドに設定されました。
public ActionResult Index(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
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);
}
return View(movies);
}
パラメーター "All" はオプション ラベルを示します。 ブラウザーでその選択肢を調べると、その "value" 属性が空であることがわかります。 コントローラーでは if
がフィルター処理されるだけで文字列は null
や空ではないため、空の値を movieGenre
に送信するとすべてのジャンルが表示されます。
既定で選択するオプションを設定することもできます。 既定のオプションとして "Comedy" が必要な場合は、コントローラーのコードを次のように変更します。
ViewBag.movieGenre = new SelectList(GenreLst, "Comedy");
アプリケーションを実行し、/Movies/Index を参照します。 ジャンル、映画名、および両方の条件で検索してみてください。
このセクションでは、ユーザーにタイトルとジャンルでの映画検索機能を提供する検索アクション メソッドとビューを作成しました。 次のセクションでは、Movie
モデルにプロパティを追加する方法と、テスト データベースを自動的に作成する初期化子を追加する方法について説明します。