3.アプリケーションロジック(Controller)への適用
それでは実際にリポジトリクラスをControllerクラスへ適用してみましょう。
最初に設定するのはコンストラクタです。通常実行時用と、単体テスト用のコンストラクタの2種類を設定します。
using System; using System.Web.Mvc; using RepositorySample.Models; namespace RepositorySample.Controllers { public class PubsController : Controller { IPubsRepository pubsrepository; public PubsController() : this(new PubsRepository()) { } public PubsController(IPubsRepository repository) { pubsrepository = repository; } <中略> } }
最初にIPubsRepositoryインターフェイスを宣言し、パラメタなしのコンストラクタが呼ばれた時、自身のオブジェクトをPubsRepositoryクラスのパラメタ付きで再生成しています。パラメタ付きのコンストラクタで、PubsRepositoryクラスをIPubsRepositoryインターフェイスに代入して利用します。続いて各項目メソッドのコードです。
Index/Detailsメソッド
Index(一覧取得)・Details(詳細表示)は値を取得後、LINQ結果を各ページに渡すだけだったので、非常にシンプルになります。
using System; using System.Web.Mvc; using RepositorySample.Models; namespace RepositorySample.Controllers { public class PubsController : Controller { IPubsRepository pubsrepository; <中略> public ActionResult Index() { // titlesエンティティのレコードを全取得 var _titles = pubsrepository.FindAllTitles(); // Index.aspxページにLINQ結果のModelを渡す return View(_titles); } // // GET: /Pubs/Details/5 public ActionResult Details(string id) { // idと一致するレコードを取得 var _titles = pubsrepository.GetDetailTitles(id); // Details.aspxページにLINQ結果のModelを渡す return View(_titles); } } }
前述したように、FindAllTitlesメソッドでエンティティ内のレコードを全取得を、GetDetailTitlesメソッドでは、idと一致するレコードを取得し返します。
Createメソッド
Create(新規レコード作成)は初回アクセス時にCreate.aspxページを表示させ、ポストアクセス時にレコードを登録します。検証部分などは第2回の内容と変わりないので割愛します。
今回はControllerクラスに検証機能を格納していますが、検証機能はControllerクラス内に置く必要はないので、リポジトリクラス内に格納するか(ただし、リポジトリはデータアクセスのみに集中させることが筆者のおススメ)または、検証クラスを作成し、そちらにすべて委譲することで、よりControllerクラス内がすっきりします。
using System; using System.Web.Mvc; using RepositorySample.Models; namespace RepositorySample.Controllers { public class PubsController : Controller { IPubsRepository pubsrepository; <中略> public ActionResult Create() { return View(); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(titles Titles, FormCollection collection) { <中略~検証コードは前回と同じです~> // 検証メッセージが格納されているかチェック(なければTrue) if (ModelState.IsValid) { try { // titlesEntityにレコードを追加する pubsrepository.Add(Titles); // Index.aspxページにリダイレクト return RedirectToAction("Index"); } catch { return View(); } } return View(); }
Createメソッドもリポジトリクラスを利用することで、データアクセス部分がすっきりしています。Model(titlesエンティティ)をパラメタとして渡しているので、既に登録予定のレコードはTitlesオブジェクトにバインドされています。バインドされている値をAddメソッドを使い、リポジトリクラス側でEDMに登録し、DBへデータ反映を行っています。
Editメソッド
Edit(レコード編集)は初回アクセス時にDetails同様の処理でページに値をバインドし、ポストアクセス時にレコードを編集します。
using System; using System.Web.Mvc; using RepositorySample.Models; namespace RepositorySample.Controllers { public class PubsController : Controller { IPubsRepository pubsrepository; <中略> public ActionResult Edit(string id) { // idと一致するレコードを取得 var _titles = pubsrepository.GetDetailTitles(id); // Edit.aspxページにLINQ結果のModelを渡す return View(_titles); } // ②編集更新ボタンクリック時 // POST: /Pubs/Edit/5 [AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(string id, FormCollection collection) { // idと一致するレコードを取得 var _titles = pubsrepository.GetDetailTitles(id); try { // 主キーとなるtitle_id以外の項目の更新があればそれを更新する UpdateModel(_titles); // 更新を反映させる pubsrepository.Save(); return RedirectToAction("Index"); } catch { return View(); } }
ポストアクセス時のEditメソッドでは、GetDetailTitlesメソッドでレコード抽出後、UpdateModelメソッドで、編集レコードの更新を行います。その後、Saveメソッドで更新を反映させ、最後にIndex.aspxページにリダイレクトしています。
Deleteメソッド
今回新規で追加するDeleteメソッドですが、ASP.NET MVCで作成する際に1点注意事項があります。それは、ActionLinkヘルパーメソッドから直接削除処理をさせないことです。
少し考えれば分かることですが、インターネット上に公開する際、検索エンジンからのクロールを意識する必要があります。もし、リンクで直接削除処理ができてしまう場合、検索エンジンのクロールにより、データがすべて消えてしまう可能性があります。そこで、DELETE機能を実装する場合の1つの手法として、ポストアクセス時に実装するということも押さえておいてください。
もう少しセキュアな方法を推奨するならば認証ユーザーしか削除できないようにすると良いでしょう(これはCreateメソッドや場合によってはIndex/Details/Editにも当てはまります)。
さて、本題です。
Delete(レコード削除)は初回アクセス時にDetails同様の処理でページに値をバインドし、ポストアクセス時にレコードを削除します。
using System; using System.Web.Mvc; using RepositorySample.Models; namespace RepositorySample.Controllers { public class PubsController : Controller { IPubsRepository pubsrepository; <中略> public ActionResult Delete(string id) { // idと一致するレコードを取得 var _titles = pubsrepository.GetDetailTitles(id); // Edit.aspxページにLINQ結果のModelを渡す return View(_titles); } // ②DELETEボタンクリック時 // POST: /Pubs/Edit/5 [AcceptVerbs(HttpVerbs.Post)] public ActionResult Delete(string id, FormCollection collection) { // idと一致するレコードを取得 var _titles = pubsrepository.GetDetailTitles(id); try { // 更新を反映させる pubsrepository.Delete(_titles); return RedirectToAction("Index"); } catch { return View(); } }
処理的に言うと、Createメソッドとほぼ同じです。違う点は、GetDetailTitlesメソッドでidと一致するレコードを取得後、Addメソッドの代わりにDeleteメソッドでレコードを削除している点です。
なお、ViewPage側は今回Add Viewダイアログで、次のように設定しています。
項目名 | 設定内容 |
View name | Delete |
Create a strongly-typed view | チェック |
View data class | RepositorySample.Models.titles |
View content | Details |
その他の項目 | 既定のまま |
ViewPage側の設定で修正している点は次の部分です。
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Delete</h2> <% using (Html.BeginForm()) {%> <p> 以下の項目を削除します。よろしいですか? <input type="submit" value="Delete" /> </p> <% } %> <以下Detailsの表示部分>
BeginFormヘルパーメソッドを利用してポストアクセスを行っています。
Index/Details/Create/Edit/Deleteと見てきましたが、データアクセス部分とControllerを分離できていることをご確認いただけたかと思います。続いて、単体テストについて触れてみます。