次の方法で共有


検索する

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 クエリは、WhereOrderBy などのメソッドの呼び出しで定義または変更されたときには実行されません。 クエリの実行が先延ばしされるため、式の評価は、実現された値が実際に反復評価されるか ToList メソッドが呼び出される時点まで遅延されます。 Search サンプルの場合、クエリは Index.cshtml ビューで実行されます。 クエリの遅延実行の詳細については、「クエリの実行」を参照してください。

Note

Contains メソッドは上記の C# コードではなく、データベースで実行されます。 データベースでは、Contains は大文字と小文字の区別がない SQL LIKE にマッピングされます。

以上で、フォームをユーザーに表示する Index ビューを更新できるようになりました。

アプリケーションを実行し、/Movies/Index に移動します。 ?searchString=ghost などのクエリ文字列を URL に追加します。 フィルターされたムービーが表示されます。

SearchQryStr

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 セグメント) として検索タイトルを渡すことができます。

Screenshot that shows the M V C Movie Index page. Local host colon 1 2 3 4 forward slash Movies forward slash index forward slash ghost is in the U R L field and circled in red.

ただし、ユーザーがムービーを検索するたびに 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 が正しいコントローラー アクション メソッドを呼び出し、ビューが表示されます。

Screenshot that shows the Index dot c s h t m l tab and Solution Explorer open. In Solution Explorer, the subfolder Movies is open and Index dot c s h t m l is selected.

Visual Studio で (上の図に示すように) インデックス ビューを開いた状態で、Ctr F5 または F5 をタップしてアプリケーションを実行し、ムービーを検索してみてください。

Screenshot that shows the Index page with a title entered into the Title field.

Index メソッドの HttpPost オーバーロードはありません。 このメソッドはデータをフィルター処理するだけで、アプリケーションの状態を変更しないため、必要がないからです。

以下の HttpPost Index メソッドを追加できます。 この場合、アクション呼び出し元は HttpPost Index メソッドと一致し、HttpPost Index メソッドが下の図のように実行されます。

[HttpPost] 
public string Index(FormCollection fc, string searchString) 
{ 
    return "<h3> From [HttpPost]Index: " + searchString + "</h3>"; 
}

SearchPostGhost

ただし、この 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))

BeginFormPost_SM

ここで検索を送信すると、URL に検索クエリ文字列が含められます。 HttpPost Index メソッドがある場合でも、検索時には HttpGet Index アクション メソッドにも移動します。

IndexWithGetURL

ジャンル別検索を追加する

先ほど 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 ヘルパーが ViewBagIEnumerable<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 を参照します。 ジャンル、映画名、および両方の条件で検索してみてください。

Screenshot that shows the Index page. A type of genre is selected.

このセクションでは、ユーザーにタイトルとジャンルでの映画検索機能を提供する検索アクション メソッドとビューを作成しました。 次のセクションでは、Movie モデルにプロパティを追加する方法と、テスト データベースを自動的に作成する初期化子を追加する方法について説明します。