トレーニングコース。 ASP.NET MVCアプリケーションでEntity Frameworkを使用して基本的なCRUD機能を実装する

これは、Entity FrameworkおよびASP.NET MVC 3開発記事シリーズの拡張機能であり、最初の章は次のリンクにあります: ASP.NET MVCアプリケーション用のEntity Frameworkデータモデルの作成



前のレッスンでは、Entity FrameworkとSQL Server Compactを使用してデータを保存および表示できるMVCアプリケーションを作成しました。 このチュートリアルでは、MVC scaffoldがコントローラーとビューで自動的に作成するCRUD(作成、読み取り、更新、削除)機能の作成と構成について説明します。



「リポジトリ」パターンを実装して、コントローラーとデータアクセスレイヤーの間に抽象化レイヤーを作成するの一般的です。 ただし、これは後のレッスン( リポジトリと作業単位パターンの実装 )で後ほど説明します



このレッスンでは、次のページが作成されます。



imageimageimageimage



詳細ページの作成



[詳細]ページには、登録コレクションの内容がHTMLテーブルに表示されます。



Controllers \ StudentControllerで 詳細を表すcsメソッドは次のコードです。



public ViewResult Details(int id) { Student student = db.Students.Find(id); return View(student); }
      
      





Findメソッドは、メソッドに渡されたidパラメーターに対応する1つのStudentエンティティを取得するために使用されます。 id値は、リンクの[詳細]ページにあるクエリ文字列から取得されます。



ビュー \ 学生 \ 詳細を開きます。 cshtml 各フィールドは、DisplayForヘルパーによって表示されます。



 <div class="display-label">LastName</div> <div class="display-field"> @Html.DisplayFor(model => model.LastName) </div>
      
      





登録のリストを表示するには、EnrollmentDateフィールドの後、fieldset終了タグの前に次のコードを追加します。



 <div class="display-label"> @Html.LabelFor(model => model.Enrollments) </div> <div class="display-field"> <table> <tr> <th>Course Title</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @Html.DisplayFor(modelItem => item.Course.Title) </td> <td> @Html.DisplayFor(modelItem => item.Grade) </td> </tr> } </table> </div>
      
      





このコードは、登録ナビゲーションプロパティのエンティティをループします。 エンティティ登録ごとに、コース名と成績が表示されます。 コース名は、登録エンティティのコースナビゲーションプロパティに含まれるコースエンティティから取得されます。 このデータはすべて、必要に応じてデータベースから自動的にアップロードされます(つまり、遅延読み込みが使用されます。EasyLoadingをCoursesナビゲーションプロパティに指定なかったため、このプロパティに初めてアクセスすると、データを取得するためにデータベースに対してクエリが実行されます。ここでの読み込みと熱心な読み込み関連データの読み取り



[ 学生 ]タブをクリックし、 [詳細]リンクをクリックします。



image



作成ページの作成



Controllers \ StudentController.csで、HttpPost Createメソッドコードを置き換えます。



 [HttpPost] public ActionResult Create(Student student) { try { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } return View(student); }
      
      





したがって、ASP.NET MVCモデルバインダーによって作成されたStudentエンティティを対応するエンティティのセットに追加し、変更をデータベースに保存します。 ( モデル バインダーはASP.NET MVCの機能であり、フォームからのデータを簡単に操作できます。モデルバインダーは、フォームのデータを対応する.NET Frameworkデータ型に変換し、パラメーターとして目的のメソッドに渡します。この場合、モデルバインダーはFormコレクションのプロパティ値。)



try-catchブロックは、このオプションと自動的に作成されたものとの唯一の違いです。 変更の保存中にDataExceptionから継承された例外がキャッチされると、標準エラーメッセージが表示されます。 このようなエラーは通常、プログラミングエラーではなく外部の何かが原因で発生するため、ユーザーは単に再試行するように求められます。 ビュー \ 学生 \ 作成のコード cshtmlDetailsのコードに似ています EditorForとValidationMessageForを除くcshtml。DisplayForヘルパーの代わりに各フィールドに使用されます。 次のコードは例です。



 <div class="editor-label"> @Html.LabelFor(model => model.LastName) </div> <div class="editor-field"> @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName) </div>
      
      





Createで cshtmlに変更を加える必要はありません。



[ 学生 ]タブをクリックし、[ 新規 作成 ]をクリックします



image



データ検証はデフォルトで有効になっています。 名前と誤った日付を入力し、「作成」をクリックしてエラーを確認します。



image



この場合、JavaScriptを使用して実装されたクライアント側のデータ検証が表示されます。 サーバー側のデータ検証も実装されており、クライアント側のデータ検証が不正なデータをスキップした場合でも、サーバー側でキャッチされ、例外がスローされます。



日付を正しい日付(2005年9月1日など)に変更し、[ 作成 ]をクリックして、 インデックスページに新しい生徒を表示します。



image



編集ページを作成



Controllers \ StudentController.csでは 、HttpGet Editメソッド(HttpPost属性を持たないメソッド)はFindメソッドを使用して、選択したStudentエンティティを取得します。 このメソッドのコードを変更する必要はありません。



HttpPost Editメソッドのコードを次のコードに置き換えます。



 [HttpPost] public ActionResult Edit(Student student) { try { if (ModelState.IsValid) { db.Entry(student).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } return View(student); }
      
      





このコードはHttpPost Createメソッドにあったものと似ていますが、このコードはエンティティをセットに追加する代わりに、変更されたかどうかを決定するエンティティプロパティを設定します。 SaveChangesを呼び出すと、Modifiedプロパティは、データベースのレコードを更新するためにSQLクエリを作成する必要があることをEntity Frameworkに示します。 ユーザーが触れていない列も含めて、レコードのすべての列が更新されます。 並行性の問題は無視されます。 (並行性の問題については、並行性の処理を参照してください 。)



エンティティの状態とAttachおよびSaveChangesメソッド



データベースコンテキストは、データベース内の対応するエントリとメモリ内のエンティティの同期を監視し、この情報はSaveChangesメソッドが呼び出されたときに何が起こるかを決定します。 たとえば、Addメソッドに新しいエンティティを渡すと、このエンティティの状態は「追加済み」に変わります。 次に、SaveChangesメソッドが呼び出されると、データベースコンテキストがSQL INSERTクエリの実行を開始します。



エンティティの状態は次のように定義できます。



デスクトップアプリケーションでは、状態が自動的に変更されます。 このタイプのアプリケーションでは、エンティティを抽出し、一部のプロパティの値を変更します。これにより、状態が変更済みに変わります。 SaveChangesを呼び出した後、Entity Frameworkは、値が変更されたプロパティのみを更新するUPDATE SQLクエリを生成します。



ただし、Webアプリケーションでは、エンティティを取得するデータベースコンテキストインスタンスがページのリロード後に破棄されるため、アルゴリズムに違反しています。 HttpPost Editで新しいリクエストが発生し、コンテキストの新しいインスタンスがあるため、エンティティの状態を手動でModifiedに変更する必要があります。 その後、SaveChangesを呼び出すと、具体的に変更されたプロパティがコンテキストで認識されなくなるため、Entity Frameworkはデータベース内のレコードのすべての列を更新します。



Updateでユーザーが編集したフィールドのみを変更する場合、HttpPost Edit呼び出し時に元の値(たとえば、非表示のフォームフィールド)を使用できるようにすることで、何らかの方法で保存できます。 この方法では、元の値を使用してStudentエンティティを作成し、元のバージョンのエンティティでAttachメソッドを呼び出し、エンティティ値を更新してSaveChangesを呼び出すことができます。 詳細については、「 Add / Attach and Entity States」および「Entity Framework Local Data development team blog post」 参照してください



ビュー \ 学生 \ 編集のコード cshtmlは Createのコードに似ています cshtml 、変更する必要はありません。



[ 生徒 ]タブをクリックし、[ 編集]リンクをクリックします。



image



値を変更して[ 保存 ]をクリックします



image



削除ページの作成



Controllers \ StudentController.csでは 、HttpGet DeleteメソッドはFindメソッドを使用して、選択されたStudentエンティティを取得します(詳細および前述のとおり)。 SaveChanges呼び出しが失敗した場合にエラーメッセージを実装するには、メソッドと対応するビューに機能を追加する必要があります。



更新および作成操作と同様に、削除操作にも2つのメソッドが必要です。 GET要求への応答で呼び出されるメソッドは、削除を確認またはキャンセルできるビューをユーザーに表示します。 ユーザーが削除を確認すると、POST要求が作成され、HttpPost Deleteメソッドが呼び出されます。



データベースの更新時に発生する可能性のあるエラーを処理するには、HttpPost Deleteメソッドコードにtry-catch例外ブロックを追加する必要があります。 エラーが発生した場合、HttpPost DeleteメソッドはHttpGet Deleteメソッドを呼び出し、エラーを通知するパラメーターを渡します。 HttpGet Deleteメソッドは、削除確認ページとエラーテキストを再度生成します。



エラーを処理するには、HttpGet Deleteメソッドのコードを次のコードに置き換えます。



 public ActionResult Delete(int id, bool? saveChangesError) { if (saveChangesError.GetValueOrDefault()) { ViewBag.ErrorMessage = "Unable to save changes. Try again, and if the problem persists see your system administrator."; } return View(db.Students.Find(id)); }
      
      





このコードは、エラーを通知するオプションのブール型パラメーターを受け入れます。 このパラメーターは、HttpGet Deleteを呼び出した後はnull(false)、HttpPost Deleteを呼び出した場合はtrueです。



HttpPost Delete(DeleteConfirmed)メソッドコードを、エラーを削除して処理する次のコードに置き換えます。



 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { try { Student student = db.Students.Find(id); db.Students.Remove(student); db.SaveChanges(); } catch (DataException) { //Log the error (add a variable name after DataException) return RedirectToAction("Delete", new System.Web.Routing.RouteValueDictionary { { "id", id }, { "saveChangesError", true } }); } return RedirectToAction("Index"); }
      
      





コードは選択されたエンティティを返し、Removeメソッドを呼び出して、エンティティの状態をDeletedに変更します。 SaveChangesを呼び出すと、SQL DELETEクエリが生成されます。



パフォーマンスが優先される場合は、FindおよびRemoveメソッドを呼び出すコードを置き換えることにより、レコードを返す不要なSQLクエリなしで実行できます。



 Student studentToDelete = new Student() { StudentID = id }; db.Entry(studentToDelete).State = EntityState.Deleted;
      
      





このコードでは、主キー値のみを使用してStudentエンティティをインスタンス化し、エンティティの状態を削除済みとして定義します。 これは、Entity Frameworkがエンティティを削除するために必要なすべてです。



既に述べたように、HttpGet Deleteメソッドはデータを削除しません。 GETリクエストへの応答でデータを削除(または、データを編集、作成、その他のアクションを実行)すると、セキュリティ違反が発生します。 詳細については、 ASP.NET MVCヒント#46-リンクを削除しないでください 。StephenWaltherのブログでセキュリティホール作成されるためです。



Views \ Student \ Deleteで cshtmlは、h2とh3の間に次のコードを追加します。



<p class = "error"> @ ViewBag.ErrorMessage </ p>



[ 生徒 ]タブをクリックし、[ 削除]リンクをクリックします。



image



削除をクリックします。 インデックスページが読み込まれます。既に削除した生徒は含まれていません( 同時処理の処理のレッスンのメソッドでコード処理の例が表示されます)。



開いているデータベース接続が残っていないことを確認します



データベースへのすべての接続が正しく閉じられ、それらによって占有されているリソースが解放されたことを確認するには、コンテキストが破棄されることを確認する必要があります。 そのため、StudentControllerのStudentControllerコントローラークラスの最後にDisposeメソッドを見つけることができます cs



 protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }
      
      





基本クラスのControllerはIDisposableインターフェイスを実装するため、このコードは単にDispose(bool)メソッドをオーバーライドして、コンテキストインスタンスを外部から削除します。



これで、Studentエンティティに対して簡単なCRUD操作を実装するすべてのものができました。 次のレッスンでは、ソートとページネーションを追加して、インデックスページの機能を拡張します。



Alexander Belotserkovsky( ahriman )の翻訳にご協力いただきありがとうございます。



All Articles