情報応用演習Ⅰ(2024)

【T5b】モデルとデータベースの連携(後編)(7/8)

プロジェクトタイプ(注意: 本文参照)
プロジェクト名T5b
ソリューション名PIT5
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

5b-7. Editアクションとビューの作成

次に,個々の学生の詳細情報を編集するためのアクションEditを作成しよう. StudentsControllerクラスに_に示すメソッドを追加する.

StudentsControllerクラスの変更内容(Editアクション(GET用))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Editアクション(GET用)
public IActionResult Edit(int? id)
{
    if (id == null)                                       // idが指定されなかったら
        return NotFound();                                // 404 Not Found

    var student = (from s in _context.Students            //  
                   where s.Id == id                       // LINQを用いてidで指定された学生情報を探す
                   select s).FirstOrDefault();            // 

    if (student == null)                                  // idで指定された学生が
        return NotFound();                                // 見つからなかったら 404 Not Found

    return View(student);                                 // 見つかった学生情報をビューに渡す
}

このEdit()メソッドはメソッド名を除けばリスト5b-6-1Details()メソッドと全く同じである. 次にこのアクション用のビューを作成する. Views/Students フォルダにEditという名前のビューを追加する. Views/Students/Edit.cshtml を_のように変更する.

Views/Students/Edit.cshtmlの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@model Student

@{
    ViewData["Title"] = "学生情報の編集";

    if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
}

<form asp-action="Edit">
  @* IDの表示(これは編集させない) *@
  <label asp-for="@Model.Id"></label>:
  <input type="hidden" asp-for="@Model.Id" />
  @Html.DisplayFor(s => s.Id)
  <br />

  @* (LastName) のための入力欄 *@
  <label asp-for="@Model.LastName"></label>:
  <input asp-for="@Model.LastName" />
  <span asp-validation-for="@Model.LastName"></span>
  <br />

  @* (FirstName) のための入力欄 *@
  <label asp-for="@Model.FirstName"></label>:
  <input asp-for="@Model.FirstName" />
  <span asp-validation-for="@Model.FirstName"></span>
  <br />

  @* 性別(Sex) のための入力欄 *@
  <label asp-for="@Model.Sex"></label>:
  <select asp-for="@Model.Sex" asp-items="Html.GetEnumSelectList<SexType>()"></select>
  <span asp-validation-for="@Model.Sex"></span>
  <br />

  @* 電話番号(PhoneNumber) のための入力欄 *@
  <label asp-for="@Model.PhoneNumber"></label>:
  <input asp-for="@Model.PhoneNumber" />
  <span asp-validation-for="@Model.PhoneNumber"></span>
  <br />

  @* メールアドレス(Mail) のための入力欄 *@
  <label asp-for="@Model.Mail"></label>:
  <input asp-for="@Model.Mail" />
  <span asp-validation-for="@Model.Mail"></span>
  <br />

  @* 誕生日(Birthday) のための入力欄 *@
  <label asp-for="@Model.Birthday"></label>:
  <input asp-for="@Model.Birthday" />
  <span asp-validation-for="@Model.Birthday"></span>
  <br />

  @* 登録日時(Registered)の表示(これは編集させない) *@
  <label asp-for="@Model.Registered"></label>:
  <input type="hidden" asp-for="@Model.Registered" />
  @Html.DisplayFor(s => s.Registered)
  <br />

  <input type="submit" value="送信" />
</form>

<a asp-action="Details" asp-route-id="@Model.Id">詳細に戻る</a>

@section Scripts
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

これもリスト5b-5-2の Create.cshtml とほとんど違いがない. 注目するべきは,すべての項目が編集可能になってはいないことである._の強調した二か所では, IdプロパティとRegisterプロパティに関しては単に値を表示するだけで入力欄を設けていない.

ここで重要な点は,いかなるウェブアプリケーションであっても既存のレコードを編集する際は 主キーは決して変更させない ということである. 使用しているデータベース製品にもよるが,新規レコードのための主キーはデータベースサーバ側で管理されているため, 既存レコードの主キーを自由に変更できるようにしてしまうとテーブル内のレコードとの整合性が保てなくなってしまう.

このため_の10~14行目では主キーは表示するだけで入力欄を用意していない. 52~56行目で登録日時も同様に表示するだけで入力欄を用意していないが,「登録日時」は その名の通りその学生を最初に登録した日時を記録するためのレコードであるためである. ただしこれらのデータは入力欄を作ってはいないが, type 属性に"hidden"を指定した input 要素でサーバ側に送られるようになっている .変更しないデータが無意味にアクションメソッドと ビューの間を往復することになるが,変更するデータだけをビューから送信させてそれのみを既存データに反映させるのは それほど難しいことではない.

次にこのフォームの送信に対応するアクションメソッドを用意しよう. StudentsControllerクラスに_に示すメソッドを追加する.

StudentsControllerクラスの変更内容(Editアクション(POST用))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Editアクション(POST用)
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Student student)
{
    if (id != student.Id)                                 // ルーティングパラメータと
        return NotFound();                                // 送信されたデータが食い違っていた場合は
                                                          // 404 Not Found

    if (ModelState.IsValid)                               // 入力値に誤りがない場合のみデータベースへの登録処理を行う.
    {
        _context.Update(student);                         // 学生情報を更新する.
        _context.SaveChanges();                           // データベースに変更を保存する.

        return RedirectToAction(nameof(Details), new { id = student.Id }); // Details に遷移する.
    }
    else
    {
        return View(student);                             // エラーがあった場合は元のビューに戻る
    }
}

これも,Update()メソッドを使っている点を除けばリスト5b-5-3Create()メソッドとほとんど同じである1. 15行目ではRedirectToAction()メソッドを使ってDetailsアクションに転送している. メソッドの第二引数に指定している"new { id = student.Id }"はルーティングパラメーターを指定するものである. "new { ... }"という見慣れない記述が登場しているが,これは匿名クラスという 匿名の型のオブジェクトをインラインで生成するための記法である.ここではこのアプリで使用しているルーティングパラメーターと 同名のプロパティを定義することでidパラメーターを指定している.

同じ方法を用いてCreateアクションからDetailsアクションへの遷移も実装しておこう. リスト5b-5-3の時点ではDetailsアクションを実装していなかったので,代わりにIndexアクションに遷移するようにしていたが, Controllers/StudentsController.cs のCreate()メソッド(POST用)を_に示すように変更しよう.

StudentsControllerクラスの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Createアクション(POST用)
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([Bind("FirstName, LastName, Sex, PhoneNumber, Mail, Birthday")]Student student) 
{
    if (ModelState.IsValid)
    {
        student.Registered = DateTime.Now;

        _context.Add(student);
        _context.SaveChanges();

        return RedirectToAction(nameof(Details), new { id = student.Id }); // Details に遷移する.       
    }
    else 
    {
        return View(student);
    }
}

ここまで書けたら実行してみよう.一覧に表示されている学生のリンクをクリック→「編集」のリンクをクリックして Editアクションにアクセスし,学生の詳細情報を適当に編集して「送信」ボタンをクリックしてみよう(__). Detailsアクションに遷移して,編集内容が反映されたことが分かるはずである(_).

実行結果

  1. この実装方法では,簡単のため更新の競合が発生する可能性を無視している.12行目でDbUpdateConcurrencyException例外が投じられる可能性があるので本来はこれに対応する必要がある. ↩︎

Last updated on 2024-05-10
Published on 2024-05-10

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.