しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET MVC 3 開発入門 (9) - アクションの実装

ASP.NET MVC 3 開発入門 - インデックス

それでは前回コントローラと同時に作成した基本アクションを使ってロジックを書いていきましょう。まずは比較的簡単な Details アクションから実装していきます。

Details アクション

Details アクションの機能は動画 ID を受け取り、それを元にデータベースから登録されている動画情報を取得して詳細ビューを表示することです。コントローラと同時に自動生成された Details アクションは以下のようになっています。

//
// GET: /Video/Details/5

public ActionResult Details(int id)
{
    return View();
}

骨組みは完成していて、後は id の値を使ってデータベースから動画情報を取得してビューを返す処理ですが、まずはデータベースから動画情報を取得しましょう。
データベースへの操作はリポジトリパターンの適用時に作成した VideoRepository クラスを経由して行います。前回で VideoController クラスに_videoRepository フィールドを追加しているので、これを利用して動画情報を取得しましょう。

//
// GET: /Video/Details/5

public ActionResult Details(int id)
{
    var video = _videoRepository.Find(id);

    return View(video);
}

これで動画情報は取得出来ました。最後にビューを返すのですが、ここで返すのは ActionResult を継承した ViewResult という型のインスタンスになります。当然ながらこれ以外にも JavaScriptResult や JsonResult などが最初から用意されていますし、自分で新しい ActionResult を継承した型を作成することも可能です。

これまでメソッドの終わりに

return View();

とおまじないのように最後に書いてきましたが、これはそのアクションのデフォルトビューを表示する ViewResult を作成して返すコードとなっています。

そして、アクションのデフォルトビューとは以下のようなパスに存在する ASPX, CSHTML, VBHTML などのファイルになります。

アクションのデフォルトビューのパス
Views/[コントローラ名]/[ビュー名(アクション名)].(aspx/cshtml/vbhtml)

↓ 上のパスにファイルが存在しないときには...

共有ビューのパス
Views/Shared/[ビュー名(アクション名)].(aspx/cshtml/vbhtml)

何だかよくわからない説明になってしまったので、View メソッドのオーバーロードの一部を見てみましょう。

  1. デフォルトのビューを表示
    • View();
  2. デフォルトのビューのモデルを指定して表示
    • View(model);
  3. 表示するビューの名前を指定して表示
    • View("OtherView");
  4. 表示するビューの名前を指定し、モデルを指定して表示
    • View("OtherView", model);

よく使うのは 1 と 2 で model を指定した場合には、ビューからは Model プロパティを経由して値を利用することが出来ます。先程の例ではリポジトリ経由で取得した動画情報をモデルとしてビューに渡しましたので、後はその値を利用してビューのレンダリングを行うだけとなります。

これで Details アクションは完成しましたが、他にも実装する必要のあるアクションが残っていますので順に実装していきましょう。

Index アクション

Index アクションにはページャを実装しようと思いますので、引数には page パラメータを追加します。page パラメータは int 型なので、先程説明したようにデフォルト値を指定しておきましょう。

//
// GET: /Video/

public ActionResult Index(int page = 1)
{
    var videos = _videoRepository.GetPagedList(page, 10);

    return View(videos);
}

後はリポジトリからページング済みのエンティティのコレクションを取得して、ビューに渡せば完成です。

Create アクション

Create アクションでは入力フォームの表示と POST で送信するデータを受け取るためで2つのアクションを用意する必要があります。HTTP メソッドによって使われるアクションを決定するために AcceptVerbs 属性が用意されていましたが、MVC 2 からは

  • HttpGet
  • HttpPost
  • HttpPut
  • HttpDelete

という4つの属性が追加されました。この属性をアクションに付けると、その HTTP メソッドでの呼び出し時のみ選択されるようになるのでメソッドごとに挙動を変えることが簡単にできます。

//
// GET: /Video/Create

public ActionResult Create()
{
    return View();
} 

//
// POST: /Video/Create

[HttpPost]
public ActionResult Create(Video video)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return View(video);
        }

        // 2011/03/01 追加、更新日時をセット
        video.UpdatedAt = video.CreatedAt = DateTime.Now;

        _videoRepository.Add(video);
        _videoRepository.Save();

        return RedirectToAction("Index");
    }
    catch
    {
        return View(video);
    }
}

POST 時に呼び出されるアクションでは引数で Video 型のパラメータを受け取ります。この値はモデルバインダによって自動的にプロパティにバインドされ、ModelState.IsValid が false の時には検証エラーが発生したということなので、これ以降の処理は行わずにビューを返します。

検証に成功した場合はリポジトリを利用してデータベースへ追加を行った後に、Index アクションへリダイレクトを行います。リポジトリへのエンティティの追加は Add メソッドを呼び出した後に、必ず Save メソッドを呼び出す必要があります。Save メソッドは内部で DbContext.SaveChanges メソッドを呼び出していて、このメソッドはデータコンテキストに対して行われた操作を実際にデータベースへ反映を行います。

したがって Save メソッドを呼び出さないと何回やってもエンティティは追加されないことになります。

そういえば、動画ファイルのアップロード機能を実装していないです。ファイルのアップロード処理については次回、単独の記事としてしっかりと説明を行う予定です。

Edit アクション

処理的には Create アクションとほぼ変わりませんが、フォームに初期値が入力されている点とデータベースへ新たにエンティティを追加するのではなくて、登録されているエンティティを更新するという点が異なっています。

したがってどのエンティティを更新するのかを知る必要があるので、アクションには id パラメータを追加しています。ちなみに FormCollection というパラメータを追加しているのは同じ名前、同じ引数のメソッドはクラスに追加することが出来ないからです。ちなみに POST されたときにはフォームの値が格納されるようになっています。

//
// GET: /Video/Edit/5
 
public ActionResult Edit(int id)
{
    var video = _videoRepository.Find(id);

    return View(video);
}

//
// POST: /Video/Edit/5

[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    var video = _videoRepository.Find(id);

    try
    {
        UpdateModel(video);

        // 2011/03/01 更新日時をセット
        video.UpdatedAt = DateTime.Now;

        _videoRepository.Save();
 
        return RedirectToAction("Index");
    }
    catch
    {
        return View(video);
    }
}

実装としてはフォームを表示するアクションは Details アクションと全く同じですが、POST 時に呼び出されるアクションでは、引数の id パラメータを使ってリポジトリからエンティティを取得して、UpdateModel メソッドを呼び出してエンティティを入力された値で更新した後に Save メソッドを呼び出して変更をデータベースへ反映しています。UpdateModel メソッドは検証に失敗した場合は例外を投げるので、try 〜 catch で囲んでいます。

エンティティに対する変更は全て Entity Framework が追跡しているので、特に追加の処理を行うことなく Save メソッドを呼び出すだけでデータベースへ反映することが出来ます。

Delete アクション

Delete アクションは Edit アクションとほぼ同じですが、Edit アクションで UpdateModel を呼び出していた部分が無くなって、Remove メソッドの呼び出しに代わっているだけです。

//
// GET: /Video/Delete/5
 
public ActionResult Delete(int id)
{
    var video = _videoRepository.Find(id);

    return View(video);
}

//
// POST: /Video/Delete/5

[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
    var video = _videoRepository.Find(id);

    try
    {
        _videoRepository.Remove(video);
        _videoRepository.Save();
 
        return RedirectToAction("Index");
    }
    catch
    {
        return View(video);
    }
}

忘れずに Save メソッドを呼び出してデータベースへの反映を行ってくださいね。

これで VideoController コントローラのアクション全ての実装が完了しました。残りの CommentController と TagController も同じように作成していくのですが、省略させていただきます。
次回は動画ファイルのアップロード機能の実装を行いたいと思います。お疲れ様でした。