WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

親子関係のあるデータの編集・削除

by WebSurfer 2014年12月22日 12:08

先に、(1) Entity Framework Code First の機能を利用して MVC4 アプリケーション用 SQL Server DB のテーブル作成、(2) Create アクションメソッドとビューを追加して作成したテーブルに親子関係のあるデータを登録する方法・・・という記事を書きました。

今回はそれに続いて Edit, Delete アクションメソッドとビューを追加し、先に追加した Create アクションメソッドで登録したデータの編集・削除を行う方法を書きます。(下の画像は Delete 操作のときのものです)

データの削除画面

先の記事 MVC4 EF Code First で書きましたように、Code First の機能を用いて生成した SQL Server DB の Parents, Children テーブルの間に外部キー制約が設定されています(Parents の Id ← Children の Parent_Id)。

なので、階層更新が必要になります。つまり、登録する場合は Parents テーブルに親レコードを INSERT した後 Children テーブルに子レコードを INSERT する、削除する場合は先に Children テーブルの関連する子レコードを全部 DELETE してから Parents テーブルの親レコードを DELETE するという操作が必要になります。

そのために Entity Framework 上で必要な操作としては、対象となるレコードの エンティティオブジェクトの状態 を、登録なら Added、編集なら Modified、削除なら Deleted としてマークし、DbContext.SaveChanges メソッド を適用すればよさそうです。

階層更新(上の例で言うと、INSERT, DELETE するときの順番)や、INSERT 時に Parents テーブルの親レコードの Id(IDENTITY 列)から値を取得して Children テーブルの子レコードの Parent_Id に設定するという操作は Entity Framework が面倒を見てくれるようです。(それを書いた公式文書が見つけられず、検証した結果だけ見てそう言っているので、100% の自信はないですけど・・・)

従って、プログラマが行うべきことで重要なのは、対象エンティティオブジェクトの状態を正しく Added, Modified, Deleted に設定してやると言うことになります。

具体的な方法は、文章で書くよりはコードを示した方がわかりやすいと思いますので、Edit, Delete 操作用のアクションメソッドと View のコードを下にアップしました。

自分が犯した失敗例や、必要な(と自分が思った)コメントも書いておきました。参考になれば幸いです。

Edit アクションメソッド

注意事項はコード内のコメントに書きましたので、それを参照してください。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc4App2.Models;

namespace Mvc4App2.Controllers
{
  public class ParentChildController : Controller
  {
    private ParentChildContext db = new ParentChildContext();
        
    //・・・中略・・・

    //
    // GET: /ParentChild/Edit/5
    public ActionResult Edit(int id)
    {
      Parent parent = db.Parents.Find(id);
      if (parent == null)
      {
        return HttpNotFound();
      }
      return View(parent);
    }

    //
    // POST: /ParentChild/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(int id, Parent postedParent)
    {
      if (ModelState.IsValid)
      {
        // 以下のコードはダメ。
        // ポストされた Name の子レコードが新手に作られて
        // INSERT され、その Parent_Id が親の Id に設定さ
        // れる。既存の子レコードの Parent_Id は NULL に
        // 書き換えられる。

        //Parent parent = db.Parents.Find(id);
        //UpdateModel<Parent>(parent);
        //db.SaveChanges();

  
        // 以下のコードもダメ。
        // 親レコードしか書き換えられない。

        //db.Entry(postedParent).State = EntityState.Modified;
        //db.SaveChanges();


        // 以下のように子の方のエンティティ状態も 'Modified'
        // に設定すると親も子も期待通り更新される。

        for (int i = 0; i < postedParent.Children.Count; i++)
        {                    
          db.Entry(postedParent.Children[i]).State = 
                                       EntityState.Modified;
        }
        db.Entry(postedParent).State = EntityState.Modified;
        db.SaveChanges();

        return RedirectToAction("Index");
      }
      return View(postedParent);
    }

    //・・・中略・・・

  }
}

View (Edit.cshtml)

Parents, Children とも Id が隠しフィールド(@Html.HiddenFor)に設定されている点に注意してください。

コードの最後の方の @Scripts.Render("~/bundles/jqueryval") は入力検証用の jQuery ライブラリを登録するためのものです。これがないとクライアント側での検証はかかりませんので注意してください。

@model Mvc4App2.Models.Parent

@{
  ViewBag.Title = "Edit";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
  @Html.AntiForgeryToken()
  @Html.ValidationSummary(true)

  <fieldset>
    <legend>Parent</legend>

    @Html.HiddenFor(model => model.Id)

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

    <hr />

    @for (int i = 0; i < Model.Children.Count; i++)
    {       
      @Html.HiddenFor(model => model.Children[i].Id)
            
      <div class="editor-label">
        @Html.LabelFor(model => 
                        model.Children[i].Name)
      </div>
      <div class="editor-field">
        @Html.EditorFor(model => 
                        model.Children[i].Name)
        @Html.ValidationMessageFor(model => 
                        model.Children[i].Name)
      </div>
                
      <hr />
    }

    <p>
      <input type="submit" value="Save" />
    </p>
  </fieldset>
}

<div>
  @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
  @Scripts.Render("~/bundles/jqueryval")
}

Delete アクションメソッド

注意事項はコード内のコメントに書きましたので、それを参照してください。


    //・・・前略・・・

    //
    // GET: /ParentChild/Delete/5
    public ActionResult Delete(int id)
    {
      Parent parent = db.Parents.Find(id);
      if (parent == null)
      {
        return HttpNotFound();
      }
      return View(parent);
    }

    //
    // POST: /ParentChild/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(int id)
    {
      // 以下のコードはダメ。
      // 子のデータががある場合、FK 制約に引っかかって
      // SqlException がスローされる。

      //Parent parent = db.Parents.Find(id);
      //db.Parents.Remove(parent);

            
      // 以下のコードもダメ。
      // 親と1 つ目の子レコードは削除されるが 2 つ目が残ってし
      // まう。(ただし残った子レコードの Parent_Id は NULL に
      // 書き換えられるので FK 制約には引っかからない。何故?)
      // Remove すると、その度 parent.Children.Count が一つ減っ
      // てしまう。そのため 1 つ目の子レコードを Remove した後
      // ループを抜けてしまい、2 つ目が Remove できないので、
      // db.SaveChanges() しても 2 つ目が残ってしまう。

      //Parent parent = db.Parents.Find(id);
      //for (int i = 0; i < parent.Children.Count; i++)
      //{
      //    db.Children.Remove(parent.Children[i]);
      //}
      //db.Parents.Remove(parent);            
      //db.SaveChanges();


      // 以下のように一旦 Child のコレクションを保持しておき、
      // それを使って Remove すれば OK。

      Parent parent = db.Parents.Find(id);

      List<Child> children = new List<Child>();
      foreach (Child child in parent.Children)
      {
        children.Add(child);
      }

      foreach (Child child in children)
      {
        db.Children.Remove(child);
      }
      db.Parents.Remove(parent);            
      db.SaveChanges();

      return RedirectToAction("Index");
    }

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

View (Delete.cshtml)

form 要素には action="/ParentChild/Delete/21" というように設定されます(21 は親レコードの Id)。なので、[Delete]ボタンをクリックして POST すると、アクションメソッド DeleteConfirmed(int id) の引数には親レコー��の Id が渡されます。

@model Mvc4App2.Models.Parent

@{
  ViewBag.Title = "Delete";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<fieldset>
  <legend>Parent</legend>
  <div class="display-label">
     @Html.DisplayNameFor(model => model.Id)
  </div>
  <div class="display-field">
    @Html.DisplayFor(model => model.Id)
  </div>
  <div class="display-label">
     @Html.DisplayNameFor(model => model.Name)
  </div>
  <div class="display-field">
    @Html.DisplayFor(model => model.Name)
  </div>

  <hr />

  @for (int i = 0; i < Model.Children.Count; i++)
  {       
    <div class="display-label">
      @Html.DisplayNameFor(model => 
                      model.Children[i].Id)           
    </div>
    <div class="display-field">
      @Html.DisplayFor(model => 
                      model.Children[i].Id)
    </div>
        
    <div class="display-label">
      @Html.DisplayNameFor(model => 
                      model.Children[i].Name)           
    </div>
    <div class="display-field">
      @Html.DisplayFor(model => 
                      model.Children[i].Name)
    </div>
    <hr />
  }

</fieldset>
@using (Html.BeginForm()) {
  @Html.AntiForgeryToken()
  <p>
    <input type="submit" value="Delete" /> |
    @Html.ActionLink("Back to List", "Index")
  </p>
}

次の課題は、(1) 既存の親に属する子の全部または一部を DELETE、(2) 既存の親に子を追加・・・をどう実装するかですね。やる気が湧いてきたら書いてみます。(笑)

さらなる課題は、同時実行制御や、エラーの際のロールバックをどう実装するかでしょうか。そこのところは勉強不足でまだ見当さえついてません。先はずいぶん長そうです。(汗)

2016/9/12 追記:
Microsoft の文書「Code First の規約」に従って、Child クラスにナビゲーションプロパティと外部キープロパティを定義するとどのような影響があるかを別の記事「Code First で外部キープロパティの定義」に書きました。int 型の外部キープロパティを Child クラスに追加にしたのですが、それによる大きな影響は以下の 2 点です。他にも影響はありますが、詳しくは上にリンクを張った別記事を見てください。
  • 編集・更新: Child クラスに外部キープロパティを追加したので、編集結果を送信して更新をかける際、外部キープロパティの値も送信する必要がある。そうしないと参照整合性制約違反でエラー。
  • 削除: 外部キープロパティを int 型にしたので、外部キーフィールドが NULL 不可になり、連鎖削除が可能になる。

Tags: ,

MVC

親子関係のあるデータ登録

by WebSurfer 2014年12月21日 15:59

先の記事 MVC4 EF Code First では、Entity Framework Code First の機能を利用して、MVC4 インターネットアプリケーションの SQL Server データベースに Parents と Children という親子関係を持つ 2 つのテーブルを作成しました。

そのアプリケーションに Create アクションメソッドとビューを追加して、Parents テーブルと Children テーブルに親と子のデータを同時に登録する方法について書きます。

Create

クライアントから上のようなユーザー入力画面を使って送信されてくるデータ(上の例では "日本太郎"、"日本花子"、"日本一郎" という 3 つの文字列)を、Web サーバーで受け取って、Create アクションメソッドのパラメータにバインド(モデルバインディング)してやる必要があります。

モデルバインディングとデータアノテーション検証(クライアントサイドを含む)が正しく行われるためには、以下の 2 つの点を考慮する必要があります。

  1. input 要素の name 属性はデータがコレクションの場合 "prefix[index].Property" というパターンにする。
  2. クライアント側での検証に必要な属性が追加されるよう、ビューに EditorFor のような Html ヘルパーを使う。

詳しくは、先の記事「コレクションのデータアノテーション検証」に書きましたので、興味がありましたら読んでください。

上の 2 点を考慮に入れて作った Controller の Create アクションメソッドと View のコードを下にアップしましたので見てください。

プラウザから /ParentChild/Create を GET 要求すると一番上の画像のように表示されます。そこでデータを入力して[Create]ボタンをクリックすると、クライアント側で入力データが検証されたあと、 [HttpPost] 属性を付与した方の Create アクションメソッドに POST されます。

下の画像(Visual Stidio でのデバッグ画面)を見てください。POST されてきたデータは Create アクションメソッドの parent パラメータに正しくモデルバインディングされています。

Create

その際、付与したアノテーション属性(この記事の例では Parent, Child クラスの Name プロパティに付与した Required と StringLength)によってサーバー側で入力データの検証が行われ、その結果が ModelStateDictionary(Controller.ModelState プロパティで取得できます)に格納されます。

検証結果が OK(ModelState.IsValid が true)であれば、送信されてきたデータは SQL Server の Parents, Children テーブルに登録されます。

Controller

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc4App2.Models;

namespace Mvc4App2.Controllers
{
  public class ParentChildController : Controller
  {
    private ParentChildContext db = new ParentChildContext();
        
    //・・・中略・・・

    //
    // GET: /ParentChild/Create
    // 引数 number は登録する子のレコード数。
    // 今回はとりあえずデフォルトで 2 としてみた。
    public ActionResult Create(int number = 2)
    {
      Parent parent = new Parent();
      for (int i = 0; i < number; i++)
      {
        Child child = new Child();
        parent.Children.Add(child);
      }
      return View(parent);
    }

    //
    // POST: /ParentChild/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Parent parent)
    {
      if (ModelState.IsValid)
      {
        db.Parents.Add(parent);

        // 2016/9/6 追記:
        // 以下のコードは不用でした。db.Parents.Add(parent) だけ
        // で親子両方の EntityState が Added になり、SaveChanges
        // メソッドで DB に親子とも INSERT されます。
        //for (int i = 0; i < parent.Children.Count; i++)
        //{
        //  db.Children.Add(parent.Children[i]);
        //}

        db.SaveChanges();
        return RedirectToAction("Index");
      }
      return View(parent);
    }

    //・・・中略・・・
  }
}

今回は簡略化のため、とりあえず、親 : 子 = 1 : 2 で固定としました。 1 : n で n をユーザーが設定できるようにする方法も別途書く予定です。

SQL Server の Parents テーブルの Id 列と Children テーブルの Parent_Id 列には外部キー制約が設けられていますので、先に Parents に親レコードを INSERT してから Children に子レコードを INSERT する必要があります(つまり階層更新が必要)。また、Id 列は IDENTITY なので INSERT 操作が完了するまで値が分からないという問題もあります。そのあたりは、仕組みは不明ですが、Entity Framework がうまくやってくれるようで、上のコードで問題なく Create できます。

View

@model Mvc4App2.Models.Parent

@{
  ViewBag.Title = "Create";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Create</h2>

@using (Html.BeginForm()) {
  @Html.AntiForgeryToken()
  @Html.ValidationSummary(true)

  <fieldset>
    <legend>Book</legend>

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

    <hr />
    
    @for (int i = 0; i < Model.Children.Count; i++)
    {       
      <div class="editor-label">
        @Html.LabelFor(model => model.Children[i].Name)
      </div>
      <div class="editor-field">
        @Html.EditorFor(model => model.Children[i].Name)
        @Html.ValidationMessageFor(model => 
                            model.Children[i].Name)
      </div>

      <hr />
    }

    <p>
      <input type="submit" value="Create" />
    </p>
  </fieldset>
}

<div>
  @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
  @Scripts.Render("~/bundles/jqueryval")
}

EditorFor(model => model.Name) からは name="Name"、EditorFor(model => model.Children[i].Name) からは name="Children[i].Name"(i は連番)という name 属性が生成されます。

SQL Server の Parents, Children テーブルで、Id 列はいずれも IDENTITY となっています。ユーザー入力は不要なのでその入力用の EditorFor ヘルパーは設けていません。

コードの最後の方の @Scripts.Render("~/bundles/jqueryval") はクライアント側でのユーザー入力検証用の jQuery ライブラリを登録するためのものです。これがないとクライアント側での検証はかかりませんので注意してください。


以上、とりあえず Create するまでを書きました。後日、別途、Edit および Delete する方法も書く予定です。階層更新は Delete がちょっと面倒な気がします。

Tags:

MVC

MVC4 EF Code First

by WebSurfer 2014年12月20日 20:41

今さらながらですが、ASP.NET MVC ベースの Web アプリケーションを開発する際、Entity Framework の Code First の機能を使って SQL Server データベースを生成してみましたので、その話を備忘録として書いておきます。

生成した DB 内容の表示

基本的には @IT の記事「第2回 Entity Frameworkコード・ファーストでモデル開発」に書いてあった通りの方法で作成してみました。

ただし、環境は MVC3 ⇒ MVC4、EF4 ⇒ EF6、SQL Server Compact 4.0 ⇒ SQL Server 2008 Express と異なります。

また、作成するテーブルも少し簡略化しました。(もちろん @IT の記事の通りに SQL Server にテーブルを作成できます。簡略化したのは、後日、今���作成したデータベースを使って別の記事を書くためです)

Visual Studio 2010 への MVC4 のインストール方法、ベースとなる MVC4 のインターネットアプリケーションの説明については以下のページを見てください。

Entity Framework Code First の機能を使えば、結果として生成されるデータベースのスキーマ等は考える必要はなくなるというような話を聞きますが、個人的には、そんなことはないと思ってます。

単一のテーブルならそうかもしれませんが、今回説明する例のように、Code First の機能によって 2 つのテーブルを生成し、それに外部キー制約がついているような場合は 階層更新 を考えなければなりません。

まぁ、それは次の記事の話として書くとして、今回は Code First の機能を利用して、外部キー制約のある 2 つのテーブルを作るところまでを書きます。

Model

まず、テーブル生成の大元になる Model ですが、以下の例を考えます。親子関係にある Parent クラス と Child クラスおよび ParentChildContext クラスに注目してください。ParentChildInitializer クラスは初期化のためのもの(イニシャライザ)です。

ParentChildContext クラスに設定されている接続文字列名 "DefaultConnection" は、Visual Studio の「MVC4 のインターネットアプリケーション」テンプレートでアプリケーションを作成すると自動的に web.config に設定されるものです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
using System.Data.Entity;

namespace Mvc4App2.Models
{
  public class ParentChildInitializer : 
      DropCreateDatabaseIfModelChanges<ParentChildContext>
  {
    protected override void Seed(ParentChildContext context)
    {
      // とりあえずレコードを 2 つ作成してみた。
      // Id は IDENTITY になるので設定不用。
      var parents = new List<Parent> {
          new Parent { Name = "親の名1" },
          new Parent { Name = "親の名2" },
      };

      parents.ForEach(p => context.Parents.Add(p));
      context.SaveChanges();
    }
  }    
    
  public class ParentChildContext : DbContext
  {
    // 接続文字列名 DefaultConnection を指定。
    // 指定しないとクラス名 ParentChildContext
    // で探しにいくので注意。
    public ParentChildContext()
          : base("DefaultConnection")
    {
    }

    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }
  } 
    
  public class Parent
  {
    public Parent()
    {
      Children = new List<Child>();
    }

    public int Id { get; set; }

    [Required(ErrorMessage = "{0} は必須")]
    [StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
    [Display(Name = "Parent Name")]
    public string Name { get; set; }

    public virtual IList<Child> Children { get; set; }
  }

  public class Child
  {
    public int Id { get; set; }

    [Required(ErrorMessage = "{0} は必須")]
    [StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
    [Display(Name = "Child Name")]
    public string Name { get; set; }
  }
}

ParentChildContext クラスに定義されている Parents, Children プロパティが DB 上のテーブルに、Parent クラス と Child クラスがそれぞれのテーブル内のレコードに該当します。

Parent, Child クラスに定義されている Id という名前のプロパティが DB 上のテーブルでは主キーとして設定されます。int 型の場合は自動的に IDENTITY になりますので注意してください。

また、Name という名前のプロパティは、プロパティに付与した型 string と属性 Required(...), StringLength(5, ...) の設定により、DB のテーブルには Name という名前の nvarchar(5)、NULL 不許可のフィールドとして生成されます。 (参考にした @IT の記事に "null を許容する値型の列は、null 許容型で指定する" と書いてあったんですが、Required(...) 属性の設定が優先されるようです)

特に重要なのが、Parent クラスの中で virtual として宣言されている Chidren プロパティです。

これは「ナビゲーションプロパティ」と呼ばれるもので、上に紹介した @IT の記事によると "エンティティ上で virtual キーワードの付いたプロパティは、それが遅延ロードされることを表す。つまり、明示的に該当するプロパティにアクセスするまで、参照先の値はデータベースから取得されない" というものだそうです。

逆に言えば、この記事の例では、プログラムで Parent クラスのナビゲーションプロパティ Children にアクセスすれば、データベースの Children テーブルから関連するレコードを取得できるということになります。

上記の Model をベースに Entity Framework Code First の機能を利用して SQL Server のデータベースに生成されるテーブルの構成は以下の画像(SQL Server Management Studio で見たもの)に示す通りです。

生成されたテーブル

aspnet-Mvc4App2-20140721201705 というデータベースは、Visual Studio の「MVC4 のインターネットアプリケーション」テンプレートでアプリケーションを作成して、ユーザー登録すると自動的に作成されるものです。(開発環境に SQL Server Express がインストールされている場合)

Children テーブルの Parent_Id という外部キーフィールドは、Parent クラスに Children というナビゲーションプロパティを定義したことによって、フレームワークが自動的に作った NULL を許容する外部キーフィールドです。Parents テーブルの Id フィールドと外部キー制約を持っています。

この記事の Child クラスには外部キープロパティを定義していませんが、Microsoft の文書「Code First 規約」の「リレーションシップ規約」セクションには "型には、ナビゲーションプロパティに加え、依存オブジェクトを表す外部キーのプロパティを追加することをお勧めします" と書いてあります。理由は、連鎖削除を設定するか否かをコントロールするためのようです。

2016/9/12 追記:
Microsoft の文書「Code First 規約」に従って、Child クラスにナビゲーションプロパティと外部キープロパティを定義するとどのような影響があるかを別の記事「Code First で外部キープロパティの定義」に書きました。外部キープロパティを int 型にしたので、外部キーフィールドはこの記事と違って NULL 不可になります。詳しくは記事を見てください。

「親の名1」「親の名2」という 2 つのフィールドは、ParentChildInitializer クラスの Seed メソッドにより初期値として設定されたものです。

Global.asax

イニシャライザ ParentChildInitializer クラスは、Global.asax の Application_Start メソッドでアプリケーションに登録します。具体例は以下の通りです。

public class MvcApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
    AreaRegistration.RegisterAllAreas();

    // ・・・中略・・・

    Database.SetInitializer<ParentChildContext>(
                           new ParentChildInitializer()); 
  }
}

ここまで済んだら、Visual Studio の[ビルド(B)]⇒[ソリューションのビルド(B)]でソリューションをビルドしておきましょう。そうしておかないと、以降の手順でモデル・クラスなどが認識されません。

実は、ここまででは、まだ SQL Server の DB にはテーブルは作成されていません。Controller と View を作って、Controller のアクションメソッドを呼び出す必要があります。

Controller と View はスキャフォールディング機能で自動的に生成できます(詳しくは上に紹介した @IT の記事を見てください)。以下に、一覧を表示する Index の部分のみ書いておきます。

Controller

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc4App2.Models;

namespace Mvc4App2.Controllers
{
  public class ParentChildController : Controller
  {
    private ParentChildContext db = new ParentChildContext();

    //
    // GET: /ParentChild/

    public ActionResult Index()
    {
      return View(db.Parents.ToList());
    }

    // ・・・中略・・・

  }
}

View

@model IEnumerable<Mvc4App2.Models.Parent>

@{
  ViewBag.Title = "Index";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>
<p>
  @Html.ActionLink("Create New", "Create")
</p>
<table>
  <tr>
    <th>
      @Html.DisplayNameFor(model => model.Id)
    </th>
    <th>
      @Html.DisplayNameFor(model => model.Name)
    </th>
        
    <th></th>
  </tr>

  @foreach (var item in Model) {
    <tr>
      <td>
        @Html.DisplayFor(modelItem => item.Id)
      </td>
      <td>
        @Html.DisplayFor(modelItem => item.Name)
      </td>
      <td>
        @Html.ActionLink("Edit", "Edit", 
            new { id=item.Id }) |
        @Html.ActionLink("Details", "Details", 
            new { id=item.Id }) |
        @Html.ActionLink("Delete", "Delete", 
            new { id=item.Id })
      </td>
    </tr>
  }
</table>

一番上の画像が、上記の Controller と View を使って表示したものです。

Tags:

MVC

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar