次の方法で共有


チュートリアル:ASP.NET MVC の Entity Framework を使用して CRUD 機能を実装する

前のチュートリアルでは、Entity Framework (EF) 6 と SQL Server LocalDB を使ってデータを保存して表示する MVC アプリケーションを作成しました。 このチュートリアルでは、MVC スキャフォールディングがコントローラーとビュー用に自動的に作成する作成、読み取り、更新、削除 (CRUD) コードを確認およびカスタマイズします。

Note

コントローラーとデータ アクセス層の間に抽象化レイヤーを作成するためにリポジトリ パターンを実装することは、よく行われることです。 この一連のチュートリアルが複雑にならないようにし、EF 6 自体の使い方に集中できるように、チュートリアルではリポジトリは使われていません。 リポジトリを実装する方法については、「ASP.NET データ アクセス コンテンツ マップ」を参照してください。

作成する Web ページの例を次に示します。

学生の詳細ページのスクリーンショット。

学生の作成ページのスクリーンショット。

学生の削除ページのスクリーンショット。

このチュートリアルでは、次の作業を行いました。

  • [詳細の作成] ページ
  • [作成] ページを更新する
  • HttpPost Edit メソッドを更新する
  • [削除] ページを更新する
  • データベース接続を閉じる
  • トランザクションを処理する

前提条件

[詳細の作成] ページ

Students Index ページのスキャフォールディングされたコードでは、Enrollments プロパティが省略されています。これは、このプロパティがコレクションを保持しているためです。 Details ページでは、コレクションの内容を HTML テーブルで表示します。

Controllers\StudentController.cs に含まれる Details ビューのアクション メソッドでは、Find メソッドを使って 1 つの Student エンティティを取得しています。

public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

キー値は id パラメーターとしてメソッドに渡され、[Index] ページの Details ハイパーリンクのルート データから取得されます。

ヒント: ルート データ

ルート データは、ルーティング テーブルで指定された URL セグメントでモデル バインダーが検出したデータです。 たとえば、既定のルートでは、controlleractionid のセグメントが指定されます。

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

次の URL では、既定のルートは Instructorcontroller として、Indexaction として、1 を id としてマッピングします。これらは、ルート データ値です。

http://localhost:1230/Instructor/Index/1?courseID=2021

?courseID=2021 はクエリ文字列の値です。 モデル バインダーは、id をクエリ文字列値として渡す場合にも機能します。

http://localhost:1230/Instructor/Index?id=1&CourseID=2021

URL は Razor ビューの ActionLink ステートメントによって作成されます。 次のコードでは、id パラメーターが既定のルートと一致するため、id がルート データに追加されます。

@Html.ActionLink("Select", "Index", new { id = item.PersonID  })

次のコードでは、courseID は既定ルートのパラメーターと一致しないため、クエリ文字列として追加されます。

@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })

[詳細] ページを作成するには

  1. Views\Student\Details.cshtml を開きます。

    次の例で示すように、DisplayFor ヘルパーを使って各フィールドが表示されます。

    <dt>
        @Html.DisplayNameFor(model => model.LastName)
    </dt>
    <dd>
        @Html.DisplayFor(model => model.LastName)
    </dd>
    
  2. 次の例で示すように、EnrollmentDate フィールドの後から、終了タグ </dl> の直前までに、登録の一覧を表示する強調表示されたコードを追加します。

    <dt>
                @Html.DisplayNameFor(model => model.EnrollmentDate)
            </dt>
    
            <dd>
                @Html.DisplayFor(model => model.EnrollmentDate)
            </dd>
            <dt>
                @Html.DisplayNameFor(model => model.Enrollments)
            </dt>
            <dd>
                <table class="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>
            </dd>
        </dl>
    </div>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
        @Html.ActionLink("Back to List", "Index")
    </p>
    

    コードを貼り付けた後でコードのインデントが乱れた場合は、Ctrl+KCtrl+D キーを押して書式設定します。

    このコードは、Enrollments ナビゲーション プロパティ内のエンティティをループ処理します。 プロパティ内の Enrollment エンティティごとに、コースのタイトルとグレードが表示されます。 コース タイトルは、Enrollments エンティティの Course ナビゲーション プロパティに格納されている Course エンティティから取得されます。 このデータはすべて、必要なときにデータベースから自動的に取得されます。 つまり、ここでは遅延読み込みを使用しています。 Courses ナビゲーション プロパティの一括読み込みを指定していないため、学生を取得したのと同じクエリで登録が取得されませんでした。 代わりに、Enrollments ナビゲーション プロパティに初めてアクセスしようとすると、新しいクエリがデータベースに送信され、データが取得されます。 遅延読み込みと一括読み込みの詳細については、このシリーズの後半の関連データの読み取りに関するチュートリアルを参照してください。

  3. プログラムを起動し (Ctrl+F5)、[学生] タブを選択して、Alexander Carson の [詳細] リンクをクリックして、[詳細] ページを開きます。 (Details.cshtml ファイルが開いているときに Ctrl+F5 キーを押すと、HTTP 400 エラーが発生します。これは、Visual Studio が [詳細] ページを実行しようとしたが、表示する学生を指定するリンクからアクセスできなかったためです。その場合は、URL から "Student/Details" を削除して再試行するか、ブラウザーを閉じてプロジェクトを右クリックし、[表示]>[ブラウザーで表示] をクリックします。)

    選んだ受講者のコースとグレードの一覧が表示されます。

  4. ブラウザーを閉じます。

[作成] ページを更新する

  1. Controllers\StudentController.csで、HttpPostAttribute Createアクション メソッドを次のコードに置き換えます。 次のコードは、try-catch ブロックを追加し、スキャフォールディングされたメソッドの BindAttribute 属性から ID を削除します。

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student)
    {
        try
        {
            if (ModelState.IsValid)
            {
                db.Students.Add(student);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
        }
        catch (DataException /* dex */)
        {
            //Log the error (uncomment dex variable name and add a line here to write a log.
            ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
        }
        return View(student);
    }
    

    このコードは、ASP.NET MVC モデル バインダーによって作成された Student エンティティを Students エンティティ セットに追加した後、変更をデータベースに保存します。 モデル バインダーとは、フォームによって送信されたデータの操作を容易にする ASP.NET MVC の機能です。モデル バインダーは、ポストされたフォーム値を CLR 型に変換して、パラメーター内のアクション メソッドに渡します。 この例のモデル バインダーは、Form コレクションからのプロパティ値を使って、Student エンティティを自動的にインスタンス化します。

    ID は行が挿入されるときに SQL Server によって自動的に設定される主キー値であるため、Bind 属性から ID を削除しました。 ユーザーからの入力によって ID 値が設定されることはありません。

    セキュリティ警告 - ValidateAntiForgeryToken 属性は、クロスサイト リクエスト フォージェリ攻撃を防ぐのに役立ちます。 ビューに対応する Html.AntiForgeryToken() ステートメントが必要です。後で説明します。

    Bind 属性は、作成シナリオでオーバーポスティング攻撃を防ぐための 1 つの方法です。 たとえば、Student エンティティに、この Web ページで設定したくない Secret プロパティが含まれているものとします。

    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
        public string Secret { get; set; }
    
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
    

    Web ページに Secret フィールドを作らなくても、ハッカーは、fiddler などのツールを使うか、何らかの JavaScript を作成して、Secret フォーム値をポストすることできます。 モデル バインダーが Student インスタンスを作成するときに使うフィールドを制限する BindAttribute 属性がないとモデル バインダーはその Secret フォーム値を取得し、それを使って Student エンティティ インスタンスを作成します。 その場合、Secret フォーム フィールドに対してハッカーが指定した値はすべて、データベースで更新されます。 次の図は、ポストされたフォームの値に、値 "OverPost" が含まれる Secret フィールドを追加している fiddler ツールを示しています。

    [Composer] タブを示すスクリーンショット。右上隅の Execute は赤で囲まれています。右下隅のシークレットは、ポストの上に等しい赤で囲まれています。

    値 "OverPost" は挿入される行の Secret プロパティに正常に追加されますが、Web ページがそのプロパティを設定できることは意図したものではありません。

    Include パラメーターを Bind 属性と共に使用して、フィールドを明示的に一覧表示することをおすすめします。 Exclude パラメーターを使用して、除外するフィールドをブロックすることもできます。 その理由 Include は、エンティティに新しいプロパティを追加しても、新しいフィールドが Exclude リストによって自動的に保護されないためです。

    最初にデータベースからエンティティを読み取り、TryUpdateModel を呼び出して明示的に許可されたプロパティ リストを渡すことにより、編集シナリオでの過剰ポスティングを防ぐことができます。 これらのチュートリアルではその方法が使われています。

    多くの開発者に好まれている、過剰ポスティングを防ぐためのもう 1 つの方法は、エンティティ クラスではなくビュー モデルをモデル バインドで使うことです。 更新するプロパティのみをビュー モデルに含めます。 MVC モデル バインダーが終了したら、必要に応じて AutoMapper などのツールを使って、ビュー モデルのプロパティをエンティティ インスタンスにコピーします。 エンティティのインスタンスで db.Entry を使ってその状態を Unchanged に設定した後、ビュー モデルに含まれる各エンティティ プロパティで Property("PropertyName").IsModified を true に設定します。 この方法は、編集シナリオと作成シナリオの両方で利用できます。

    Bind 属性以外では、スキャフォールディングされたコードに対して行った変更は try-catch ブロックだけです。 変更を保存するときに、DataException から派生した例外がキャッチされた場合は、汎用的なエラー メッセージが表示されます。 DataException 例外は、プログラミング エラーではなくアプリケーション外の何かが原因で発生する場合があるので、再試行することをお勧めします。 このサンプルでは実装されていませんが、運用品質のアプリケーションでは例外をログに記録します。 詳細については、「Monitoring and Telemetry (Building Real-World Cloud Apps with Azure)」(監視とテレメトリ (Azure での実際のクラウド アプリの構築)) の「Log for insight」(洞察のためのログ) セクションをご覧ください。

    Views\Student\Create.cshtml のコードは Details.cshtml で見たものと似ていますが、各フィールドに DisplayFor ではなく EditorFor および ValidationMessageFor ヘルパーが使用されている点が異なります。 関連するコードを次に示します。

    <div class="form-group">
        @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>
    </div>
    

    Create.cshtml には、コントローラーの ValidateAntiForgeryToken 属性と連携してクロスサイト リクエスト フォージェリ攻撃を防ぐ @Html.AntiForgeryToken() も含まれています。

    Create.cshtml では変更は必要ありません。

  2. プログラムを開始し、[学生] タブを選択し、[新規作成] をクリックして、ページを実行します。

  3. 名前と無効な日付を入力し、[Create] をクリックすると、エラー メッセージが表示されます。

    これは、既定で取得するサーバー側の検証です。 後のチュートリアルでは、クライアント側検証用のコードを生成する属性を追加する方法について説明します。 次の強調表示されたコードは、Create メソッドでのモデル検証チェックの部分です。

    if (ModelState.IsValid)
    {
        db.Students.Add(student);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    
  4. 日付を有効な値に変更し、 [Create] をクリックして、新しい学生が [Index] ページに表示されることを確認します。

  5. ブラウザーを閉じます。

HttpPost Edit メソッドを更新する

  1. HttpPostAttribute Editアクション メソッドを次のコードに置き換えます。

    [HttpPost, ActionName("Edit")]
    [ValidateAntiForgeryToken]
    public ActionResult EditPost(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        var studentToUpdate = db.Students.Find(id);
        if (TryUpdateModel(studentToUpdate, "",
           new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
        {
            try
            {
                db.SaveChanges();
    
                return RedirectToAction("Index");
            }
            catch (DataException /* dex */)
            {
                //Log the error (uncomment dex variable name and add a line here to write a log.
                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
            }
        }
        return View(studentToUpdate);
    }
    

    Note

    Controllers\StudentController.cs に含まれる HttpGet Edit メソッド (HttpPost 属性がないもの) は、Find メソッドを使って、選ばれた Student エンティティを取得します (Details メソッドと同様)。 このメソッドを変更する必要はありません。

    これらの変更は、オーバーポストを防ぐため のセキュリティのベスト プラクティスを実装します。スキャフォールディングによって Bind 属性が生成 され、モデル バインダーによって作成されたエンティティが Modified フラグを持つエンティティ セットに追加されました。 Bind 属性は、Include パラメーターにリストされていないフィールドの既存のデータをクリアするため、このコードは推奨されなくなりました。 今後、MVC コントローラー スキャフォールディングは、Edit メソッドの Bind 属性を生成しないように更新される予定です。

    新しいコードは、既存のエンティティを読み取り、TryUpdateModel を呼び出して、ポストされたフォーム データのユーザー入力からフィールドを更新します。 Entity Framework の自動変更追跡では、エンティティに EntityState.Modified フラグが設定されます。 SaveChanges メソッドが呼び出されると、Modified フラグによって、Entity Framework はデータベースの行を更新する SQL ステートメントを作成します。 コンカレンシーの競合 は無視され、ユーザーが変更しなかった列を含め、データベース行のすべての列が更新されます。 (後のチュートリアルでは同時実行の競合を処理する方法を示します。データベース内の個々のフィールドのみを更新する場合は、エンティティを EntityState.Unchanged に設定し、個々のフィールドを EntityState.Modified に設定できます。)

    オーバーポストを防ぐために、[編集] ページで更新可能にするフィールドは、TryUpdateModel パラメーターに一覧表示されます。 現在、他に保護しているフィールドはありませんが、モデル バインダーでバインドしたいフィールドをリストに入れておくと、後でデータ モデルにフィールドを追加した場合に、ここでフィールドを明示的に追加するまで、自動的にフィールドを保護できます。

    これらの変更の結果として、HttpPost Edit メソッドのシグネチャは、HttpGet Edit メソッドと同じになります。したがって、EditPost メソッドの名前を変更してあります。

    ヒント

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

    データベース コンテキストは、メモリ内のエンティティがデータベースの対応する行と同期しているかどうかを追跡しており、この情報により、SaveChanges メソッドを呼び出したときの処理が決まります。 たとえば、新しいエンティティを Add メソッドに渡すと、そのエンティティの状態は Added に設定されます。 その後、SaveChanges メソッドを呼び出すと、データベース コンテキストは SQL の INSERT コマンドを発行します。

    エンティティは、次のいずれかの状態になる可能性があります。

    • Added. エンティティはデータベースにまだ存在しません。 SaveChanges メソッドは INSERT ステートメントを発行する必要があります。
    • Unchanged. SaveChanges メソッドはこのエンティティに対し何も行う必要はありません。 データベースからエンティティを読み取ると、エンティティはこの状態で開始します。
    • Modified. エンティティのプロパティ値の一部またはすべてが変更されています。 SaveChanges メソッドは UPDATE ステートメントを発行する必要があります。
    • Deleted. エンティティには削除のマークが付けられています。 SaveChanges メソッドは DELETE ステートメントを発行する必要があります。
    • Detached. エンティティはデータベース コンテキストによって追跡されていません。

    デスクトップ アプリケーションにおいて、通常、状態の変更は自動的に設定されます。 デスクトップ タイプのアプリケーションでは、エンティティを読み取って一部のプロパティの値を変更すると、 そのエンティティの状態は自動的に Modified に変更されます。 その後、SaveChanges を呼び出すと、Entity Framework は、変更された実際のプロパティのみを更新する SQL UPDATE ステートメントを生成します。

    Web アプリは切断されているため、この連続したシーケンスには対応できません。 エンティティを読み取る DbContext は、ページがレンダリングされた後に破棄されます。 HttpPost Edit アクション メソッドが呼び出されると、新しい要求が行われ、DbContextの新しいインスタンスが作成されるため、エンティティの状態を手動で Modified. に設定する必要があります。その後、SaveChangesを呼び出すと、コンテキストで変更したプロパティを知る方法がないため、Entity Framework はデータベース行のすべての列を更新します。

    SQL Update ステートメントでユーザーが実際に変更したフィールドのみを更新する場合は、元の値 (非表示フィールドなど) を保存して、 HttpPost Edit メソッドが呼び出されたときに使用できるようにすることができます。 その後、元の値を使って Student エンティティを作成し、元のバージョンのエンティティで Attach メソッドを呼び出して、エンティティの値を新しい値に更新した後、SaveChanges. を呼び出すことができます。詳細については、「エンティティの状態と SaveChanges」および「ローカル データ」を参照してください。

    Views\Student\Edit.cshtml の HTML コードと Razor コードは、Create.cshtml で確認したコードと似ています。変更は必要ありません。

  2. プログラムを開始し、[学生] タブを選択し、[編集] ハイパーリンクをクリックして、ページを実行します。

  3. データをいくつか変更し、 [Save] をクリックします。 [Index] ページに、変更したデータが表示されます。

  4. ブラウザーを閉じます。

[削除] ページを更新する

Controllers\StudentController.cs では、HttpGetAttribute Delete メソッドのテンプレート コードでは、DetailsメソッドとEdit メソッドで確認したように、Find メソッドを使用して選択したStudent エンティティを取得します。 ただし、SaveChanges の呼び出しが失敗したときのカスタム エラー メッセージを実装するには、何らかの機能とその対応するビューをこのメソッドに追加します。

更新および作成操作で見たように、削除操作にも 2 つのアクション メソッドが必要です。 GET 要求に応答して呼び出されるメソッドは、ユーザーが削除操作を承認またはキャンセルできるビューを表示します。 ユーザーが操作を承認すると、POST 要求が作成されます。 その場合、 HttpPost Delete メソッドが呼び出され、そのメソッドが実際に削除操作を実行します。

HttpPostAttribute Delete メソッドにtry-catch ブロックを追加して、データベースの更新時に発生する可能性のあるエラーを処理します。 エラーが発生した場合、 HttpPostAttribute Delete メソッドは HttpGetAttribute Delete メソッドを呼び出し、エラーが発生したことを示すパラメーターを渡します。 次に、 HttpGetAttribute Delete メソッドは、エラー メッセージと共に確認ページを再表示し、ユーザーにキャンセルまたは再試行の機会を提供します。

  1. HttpGetAttribute Deleteアクション メソッドを、エラー報告を管理する次のコードに置き換えます。

    public ActionResult Delete(int? id, bool? saveChangesError=false)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        if (saveChangesError.GetValueOrDefault())
        {
            ViewBag.ErrorMessage = "Delete failed. Try again, and if the problem persists see your system administrator.";
        }
        Student student = db.Students.Find(id);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }
    

    このコードは、変更保存の失敗後にメソッドが呼び出されたかどうかを示す省略可能なパラメーターを受け取ります。 このパラメーターは、HttpGet Delete メソッドが以前のエラーなしで呼び出されたときにfalseされます。 データベース更新エラーに応答して HttpPost Delete メソッドによって呼び出されると、パラメーターは true され、エラー メッセージがビューに渡されます。

  2. HttpPostAttribute Deleteアクション メソッド (DeleteConfirmed という名前) を、実際の削除操作を実行し、データベース更新エラーをキャッチする次のコードに置き換えます。

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Delete(int id)
    {
        try
        {
            Student student = db.Students.Find(id);
            db.Students.Remove(student);
            db.SaveChanges();
        }
        catch (DataException/* dex */)
        {
            //Log the error (uncomment dex variable name and add a line here to write a log.
            return RedirectToAction("Delete", new { id = id, saveChangesError = true });
        }
        return RedirectToAction("Index");
    }
    

    このコードは、選択されたエンティティを取得した後、Remove メソッドを呼び出して、エンティティの状態を Deleted に設定します。 SaveChanges が呼び出された場合、SQL の DELETE コマンドが生成されます。 また、アクション メソッドの名前を DeleteConfirmed から Delete に変更しています。 HttpPost メソッドに一意のシグネチャを与えるためにDeleteConfirmedHttpPost Delete メソッドという名前のスキャフォールディングされたコード。 (CLR では、さまざまなメソッド パラメーターを持つために、オーバーロードされたメソッドを必要とします。)シグネチャが一意になっているので、MVC 規則に準拠し、HttpPostHttpGet 削除メソッドに同じ名前を利用できます。

    大規模なアプリケーションでのパフォーマンス向上が優先される場合は、FindRemove メソッドを呼び出すコード行を次のコードに置き換えることで、行を取得するための不必要な SQL クエリが実行されないようにすることができます。

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

    このコードは、主キーの値のみを使用して Student エンティティをインスタンス化し、エンティティの状態を Deleted に設定します。 エンティティを削除するために Entity Framework に必要なものは主キーの値だけです

    前に示したように、 HttpGet Delete メソッドはデータを削除しません。 GET 要求の応答で削除操作を実行すると (さらに言えば、編集操作、作成操作、データを変更するその他のあらゆる操作を実行すると)、セキュリティ上のリスクが生じます。 詳細については、Stephen Walther のブログで「ASP.NET MVC Tip #46 — Don't use Delete Links because they create Security Holes」を参照してください。

  3. Views\Student\Delete.cshtml で、次の例に示すように、h2 見出しと h3 見出しの間にエラー メッセージを追加します。

    <h2>Delete</h2>
    <p class="error">@ViewBag.ErrorMessage</p>
    <h3>Are you sure you want to delete this?</h3>
    
  4. プログラムを開始し、[学生] タブを選択し、[削除] ハイパーリンクをクリックして、ページを実行します。

  5. [削除 しますか?] というページで [削除] を選択します。

    削除された学生を含まない [インデックス] ページが表示されます。 (アクションでのエラー処理コードの例は、コンカレンシーチュートリアルをご覧ください。)

データベース接続を閉じる

データベース接続を閉じ、保持しているリソースをできるだけ早く解放するには、コンテキスト インスタンスが終了したら破棄します。 そのため、スキャフォールディングされたコードは、次の例に示すように、StudentController.csStudentController クラスの末尾に Dispose メソッドを提供します。

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

基底 Controller クラスは既に IDisposable インターフェイスを実装しているため、このコードは、コンテキスト インスタンスを明示的に破棄するために Dispose(bool) メソッドにオーバーライドを追加するだけです。

トランザクションを処理する

既定では、Entity Framework はトランザクションを暗黙的に実装します。 複数の行またはテーブルを変更してから SaveChanges を呼び出すシナリオでは、Entity Framework によって自動的に、すべての変更が成功するか、またはすべての変更が失敗することが保証されます。 一部の変更が完了した後でエラーが発生した場合、それらの変更は自動的にロールバックされます。 たとえば、Entity Framework の外部で行われる操作をトランザクションに含めたい場合など、より詳細な制御が必要なシナリオについては、「トランザクションの操作」をご覧ください。

コードを取得する

完成したプロジェクトのダウンロード

その他のリソース

Student エンティティに対して簡単な CRUD 操作を実行するページの完全なセットができあがりました。 MVC ヘルパーを使用して、データ フィールドの UI 要素を生成しました。 MVC ヘルパーの詳細については、「HTML ヘルパーによるフォームのレンダリング」を参照してください (この生地は MVC 3 用ですが、MVC 5 にも引き続き関連しています)。

他の EF 6 リソースへのリンクは、「ASP.NET データ アクセス - 推奨リソース」にあります。

次のステップ

このチュートリアルでは、次の作業を行いました。

  • [詳細の作成] ページ
  • Create ページを更新した
  • 更新された HttpPost Edit メソッド
  • Delete ページを更新した
  • データベース接続を閉じた
  • 処理されたトランザクション

次の記事に進み、プロジェクトに並べ替え、フィルター処理、ページングを追加する方法について説明します。