第20章 Webアプリで登録・更新・削除の流れを体験する
この章の目的
Section titled “この章の目的”この章では、ASP.NET Core MVCアプリからOracle Databaseに対して、登録・更新・削除を行う基本的な流れを学習します。
第19章では、Oracle Databaseから社員情報を取得し、Web画面に表示しました。
一覧表示詳細表示社員名検索部署ID検索この章では、さらに次の操作を追加します。
登録更新削除ただし、この章の目的は、本格的な業務アプリを完成させることではありません。
目的は、次の流れを体験することです。
フォームを表示する ↓利用者が値を入力する ↓Controllerが入力値を受け取る ↓RepositoryがINSERT / UPDATE / DELETEを実行する ↓一覧画面に戻るこの章で扱う範囲
Section titled “この章で扱う範囲”この章では、employees 表を使って登録・更新・削除を行います。
ただし、既存のSQL研修用データを誤って変更しないように、次のルールを設けます。
登録する社員IDは9000番台にする更新・削除は9000番台の社員のみ対象にする例:
900190029003このようにすることで、既存の社員データを残したまま、Webアプリからの更新操作を練習できます。
重要
この章ではDBのデータを変更します。 操作する前に、必ず研修用環境であることを確認してください。 本番環境や共有環境では、勝手にINSERT / UPDATE / DELETEを実行してはいけません。
挿絵・スクリーンショット案
Section titled “挿絵・スクリーンショット案”この章では、以下の画像を入れると理解しやすくなります。
図20-1 CRUD処理の全体像図20-2 登録画面図20-3 登録後に一覧へ戻る流れ図20-4 更新画面図20-5 削除確認画面図20-6 Controller、Repository、Oracle DBの役割分担画像を挿入する場合は、次のように書けます。
この章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
- CRUDとは何かを説明できる
- GETとPOSTの違いをおおまかに説明できる
- MVCで登録フォームを表示できる
- フォームから送信された値をControllerで受け取れる
INSERT文を実行してDBにデータを追加できるUPDATE文を実行してDBのデータを更新できるDELETE文を実行してDBのデータを削除できる- 入力値を簡単に検証できる
ModelStateの基本的な使い方を理解できるRedirectToActionを使って処理後に一覧画面へ戻れるTempDataを使って一時的なメッセージを表示できる- 更新・削除対象を研修用データに限定する理由を説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | ASP.NET Core Web アプリ Model-View-Controller |
| 対象フレームワーク | .NET 8 |
| プロジェクト名 | Chapter20_MvcCrud |
| DB | Oracle Database |
| 接続ユーザー | pingt |
| 使用する表 | employees、departments |
| 追加するNuGetパッケージ | Oracle.ManagedDataAccess.Core |
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- 第19章のMVCアプリからOracle Databaseに接続できる
- 社員一覧画面を表示できる
- 社員詳細画面を表示できる
-
EmployeeRepositoryの役割を説明できる -
INSERT、UPDATE、DELETEのSQLをおおまかに読める - SQLパラメーターを使う理由を説明できる
- Gitに前章の内容を提出済みである
20-1 CRUDとは?
Section titled “20-1 CRUDとは?”CRUDとは、データを扱う基本的な4つの操作です。
| 操作 | 意味 | SQL |
|---|---|---|
| Create | 登録 | INSERT |
| Read | 参照 | SELECT |
| Update | 更新 | UPDATE |
| Delete | 削除 | DELETE |
第19章では、主にReadを扱いました。
社員一覧を表示する社員詳細を表示する検索するこの章では、Create、Update、Deleteを追加します。
社員を登録する社員情報を更新する社員を削除する20-2 GETとPOST
Section titled “20-2 GETとPOST”GETは、主に画面を表示したり、検索条件を送ったりするときに使います。
例:
/Employees/Employees/Details/1001/Employees?keyword=山GETは、URLに情報が表れやすいのが特徴です。
POSTは、登録・更新・削除など、データを変更する処理でよく使います。
登録フォームを送信する更新フォームを送信する削除確認フォームを送信するこの章では、次のように使い分けます。
| 処理 | HTTPメソッド | 役割 |
|---|---|---|
| 登録画面表示 | GET | 入力フォームを表示する |
| 登録実行 | POST | DBにINSERTする |
| 更新画面表示 | GET | 既存データをフォームに表示する |
| 更新実行 | POST | DBにUPDATEする |
| 削除確認画面表示 | GET | 削除前に確認する |
| 削除実行 | POST | DBからDELETEする |
20-3 入力用Modelを作成する
Section titled “20-3 入力用Modelを作成する”EmployeeEditModelを作成する
Section titled “EmployeeEditModelを作成する”登録画面と更新画面では、利用者が入力するためのModelを使います。
Models フォルダに EmployeeEditModel.cs を作成します。
namespace Chapter20_MvcCrud.Models{ public class EmployeeEditModel { public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string? Yomi { get; set; } public int JobId { get; set; } = 1004; public int? ManagerId { get; set; } public DateTime HireDate { get; set; } = DateTime.Today; public decimal? Salary { get; set; } public decimal? Commission { get; set; } public int DepartmentId { get; set; } = 1; }}EmployeeEditModelの役割
Section titled “EmployeeEditModelの役割”EmployeeEditModel は、登録・更新フォームで使うModelです。
第19章では、一覧用に EmployeeListItem、詳細用に EmployeeDetail を作成しました。
この章では、入力用に EmployeeEditModel を使います。
EmployeeListItem 一覧表示用
EmployeeDetail 詳細表示用
EmployeeEditModel 登録・更新フォーム用画面ごとに必要なデータの形が異なるため、Modelを分けています。
20-4 Repositoryに登録処理を追加する
Section titled “20-4 Repositoryに登録処理を追加する”Insertメソッドを作成する
Section titled “Insertメソッドを作成する”EmployeeRepository に、社員を登録する Insert メソッドを追加します。
public void Insert(EmployeeEditModel employee){ string sql = @" INSERT INTO employees ( employee_id, employee_name, yomi, job_id, manager_id, hiredate, salary, commission, department_id ) VALUES ( :employeeId, :employeeName, :yomi, :jobId, :managerId, :hireDate, :salary, :commission, :departmentId )";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) { command.BindByName = true;
command.Parameters.Add("employeeId", OracleDbType.Int32).Value = employee.EmployeeId; command.Parameters.Add("employeeName", OracleDbType.Varchar2).Value = employee.EmployeeName; command.Parameters.Add("yomi", OracleDbType.Varchar2).Value = string.IsNullOrWhiteSpace(employee.Yomi) ? DBNull.Value : employee.Yomi; command.Parameters.Add("jobId", OracleDbType.Int32).Value = employee.JobId; command.Parameters.Add("managerId", OracleDbType.Int32).Value = employee.ManagerId.HasValue ? employee.ManagerId.Value : DBNull.Value; command.Parameters.Add("hireDate", OracleDbType.Date).Value = employee.HireDate; command.Parameters.Add("salary", OracleDbType.Decimal).Value = employee.Salary.HasValue ? employee.Salary.Value : DBNull.Value; command.Parameters.Add("commission", OracleDbType.Decimal).Value = employee.Commission.HasValue ? employee.Commission.Value : DBNull.Value; command.Parameters.Add("departmentId", OracleDbType.Int32).Value = employee.DepartmentId;
command.ExecuteNonQuery(); } }}ExecuteNonQueryとは
Section titled “ExecuteNonQueryとは”ExecuteNonQuery は、結果表を返さないSQLを実行するときに使います。
主に次のSQLで使います。
INSERTUPDATEDELETESELECT文では、結果を読み取るために ExecuteReader を使いました。
OracleDataReader reader = command.ExecuteReader();登録・更新・削除では、結果表を読み取る必要がないため、ExecuteNonQuery を使います。
command.ExecuteNonQuery();20-5 登録画面を作成する
Section titled “20-5 登録画面を作成する”Create Actionを追加する
Section titled “Create Actionを追加する”EmployeesController に、登録画面表示用の Create Actionを追加します。
[HttpGet]public IActionResult Create(){ EmployeeEditModel model = new EmployeeEditModel { EmployeeId = 9001, HireDate = DateTime.Today, JobId = 1004, DepartmentId = 1 };
return View(model);}[HttpGet] は、GETリクエストで呼ばれるActionであることを表します。
このActionは、登録フォームを表示するためのものです。
Create.cshtmlを作成する
Section titled “Create.cshtmlを作成する”Views/Employees フォルダに Create.cshtml を作成します。
@model Chapter20_MvcCrud.Models.EmployeeEditModel
@{ ViewData["Title"] = "社員登録";}
<h1>社員登録</h1>
<div asp-validation-summary="All" class="text-danger"></div>
<form asp-controller="Employees" asp-action="Create" method="post"> <div class="mb-3"> <label asp-for="EmployeeId" class="form-label">社員ID</label> <input asp-for="EmployeeId" class="form-control" /> <div class="form-text">研修では9000番台を使用します。</div> </div>
<div class="mb-3"> <label asp-for="EmployeeName" class="form-label">社員名</label> <input asp-for="EmployeeName" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Yomi" class="form-label">よみがな</label> <input asp-for="Yomi" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="JobId" class="form-label">職種ID</label> <input asp-for="JobId" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="ManagerId" class="form-label">上司ID</label> <input asp-for="ManagerId" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="HireDate" class="form-label">入社日</label> <input asp-for="HireDate" type="date" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Salary" class="form-label">給与</label> <input asp-for="Salary" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Commission" class="form-label">歩合</label> <input asp-for="Commission" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="DepartmentId" class="form-label">部署ID</label> <input asp-for="DepartmentId" class="form-control" /> <div class="form-text">1〜5の部署IDを指定します。</div> </div>
<button type="submit" class="btn btn-primary">登録</button> <a asp-controller="Employees" asp-action="Index" class="btn btn-secondary">戻る</a></form>asp-forとは
Section titled “asp-forとは”asp-for は、Modelのプロパティと入力欄を対応させるための書き方です。
<input asp-for="EmployeeName" class="form-control" />これは、EmployeeEditModel の EmployeeName プロパティに対応する入力欄です。
フォーム送信時には、入力された値がControllerの引数に自動的に入ります。
20-6 登録処理を作成する
Section titled “20-6 登録処理を作成する”POST用Create Actionを追加する
Section titled “POST用Create Actionを追加する”EmployeesController に、登録実行用の Create Actionを追加します。
[HttpPost][ValidateAntiForgeryToken]public IActionResult Create(EmployeeEditModel model){ ValidateEmployeeEditModel(model);
if (!ModelState.IsValid) { return View(model); }
try { EmployeeRepository repository = new EmployeeRepository();
repository.Insert(model);
TempData["Message"] = "社員を登録しました。";
return RedirectToAction(nameof(Index)); } catch (OracleException ex) { ModelState.AddModelError("", "Oracle Database処理中にエラーが発生しました。"); ModelState.AddModelError("", ex.Message);
return View(model); } catch (Exception ex) { ModelState.AddModelError("", "予期しないエラーが発生しました。"); ModelState.AddModelError("", ex.Message);
return View(model); }}ModelStateとは
Section titled “ModelStateとは”ModelState は、入力値の状態を管理する仕組みです。
入力値に問題がある場合は、次のようにエラーを追加できます。
ModelState.AddModelError("EmployeeName", "社員名を入力してください。");エラーがあるかどうかは、次のように確認できます。
if (!ModelState.IsValid){ return View(model);}エラーがある場合、登録処理は行わず、入力画面を再表示します。
入力チェック用メソッド
Section titled “入力チェック用メソッド”EmployeesController に、簡単な入力チェック用メソッドを作成します。
private void ValidateEmployeeEditModel(EmployeeEditModel model){ if (model.EmployeeId < 9000 || model.EmployeeId > 9999) { ModelState.AddModelError("EmployeeId", "研修では社員IDに9000〜9999を指定してください。"); }
if (string.IsNullOrWhiteSpace(model.EmployeeName)) { ModelState.AddModelError("EmployeeName", "社員名を入力してください。"); }
if (model.JobId <= 0) { ModelState.AddModelError("JobId", "職種IDを入力してください。"); }
if (model.DepartmentId < 1 || model.DepartmentId > 5) { ModelState.AddModelError("DepartmentId", "部署IDは1〜5で入力してください。"); }
if (model.Salary.HasValue && model.Salary.Value < 0) { ModelState.AddModelError("Salary", "給与にマイナス値は指定できません。"); }
if (model.Commission.HasValue && model.Commission.Value < 0) { ModelState.AddModelError("Commission", "歩合にマイナス値は指定できません。"); }}本格的にはDataAnnotationsを使った入力チェックもありますが、この章では処理の流れを見やすくするため、Controller内で簡単にチェックします。
TempDataとは
Section titled “TempDataとは”TempData は、次のリクエストまで一時的に値を保持する仕組みです。
TempData["Message"] = "社員を登録しました。";登録後は一覧画面へリダイレクトします。
return RedirectToAction(nameof(Index));リダイレクト後の一覧画面で、メッセージを表示できます。
Index.cshtmlにメッセージを表示する
Section titled “Index.cshtmlにメッセージを表示する”Views/Employees/Index.cshtml の上部に、次のコードを追加します。
@if (TempData["Message"] != null){ <div class="alert alert-success"> @TempData["Message"] </div>}これで、登録後に一覧画面へ戻ったとき、次のようなメッセージが表示されます。
社員を登録しました。20-7 一覧画面に登録リンクを追加する
Section titled “20-7 一覧画面に登録リンクを追加する”Index.cshtml の見出しの下などに、登録画面へのリンクを追加します。
<p> <a asp-controller="Employees" asp-action="Create" class="btn btn-primary">新規登録</a></p>これで、一覧画面から登録画面に移動できます。
20-8 更新処理を作成する
Section titled “20-8 更新処理を作成する”RepositoryにFindEditByIdを追加する
Section titled “RepositoryにFindEditByIdを追加する”更新画面では、まず既存の社員データを取得してフォームに表示します。
EmployeeRepository に、更新用データを取得するメソッドを追加します。
public EmployeeEditModel? FindEditById(int employeeId){ string sql = @" SELECT employee_id, employee_name, yomi, job_id, manager_id, hiredate, salary, commission, department_id FROM employees WHERE employee_id = :employeeId";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) { command.BindByName = true; command.Parameters.Add("employeeId", OracleDbType.Int32).Value = employeeId;
using (OracleDataReader reader = command.ExecuteReader()) { if (reader.Read()) { return new EmployeeEditModel { EmployeeId = Convert.ToInt32(reader["employee_id"]), EmployeeName = reader["employee_name"].ToString() ?? "", Yomi = reader["yomi"] == DBNull.Value ? null : reader["yomi"].ToString(), JobId = Convert.ToInt32(reader["job_id"]), ManagerId = reader["manager_id"] == DBNull.Value ? null : Convert.ToInt32(reader["manager_id"]), HireDate = Convert.ToDateTime(reader["hiredate"]), Salary = reader["salary"] == DBNull.Value ? null : Convert.ToDecimal(reader["salary"]), Commission = reader["commission"] == DBNull.Value ? null : Convert.ToDecimal(reader["commission"]), DepartmentId = Convert.ToInt32(reader["department_id"]) }; } } } }
return null;}RepositoryにUpdateを追加する
Section titled “RepositoryにUpdateを追加する”public int Update(EmployeeEditModel employee){ string sql = @" UPDATE employees SET employee_name = :employeeName, yomi = :yomi, job_id = :jobId, manager_id = :managerId, hiredate = :hireDate, salary = :salary, commission = :commission, department_id = :departmentId WHERE employee_id = :employeeId AND employee_id >= 9000";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) { command.BindByName = true;
command.Parameters.Add("employeeName", OracleDbType.Varchar2).Value = employee.EmployeeName; command.Parameters.Add("yomi", OracleDbType.Varchar2).Value = string.IsNullOrWhiteSpace(employee.Yomi) ? DBNull.Value : employee.Yomi; command.Parameters.Add("jobId", OracleDbType.Int32).Value = employee.JobId; command.Parameters.Add("managerId", OracleDbType.Int32).Value = employee.ManagerId.HasValue ? employee.ManagerId.Value : DBNull.Value; command.Parameters.Add("hireDate", OracleDbType.Date).Value = employee.HireDate; command.Parameters.Add("salary", OracleDbType.Decimal).Value = employee.Salary.HasValue ? employee.Salary.Value : DBNull.Value; command.Parameters.Add("commission", OracleDbType.Decimal).Value = employee.Commission.HasValue ? employee.Commission.Value : DBNull.Value; command.Parameters.Add("departmentId", OracleDbType.Int32).Value = employee.DepartmentId; command.Parameters.Add("employeeId", OracleDbType.Int32).Value = employee.EmployeeId;
return command.ExecuteNonQuery(); } }}WHERE employee_id >= 9000 を入れているため、既存の1000番台データは更新されません。
Edit Actionを追加する
Section titled “Edit Actionを追加する”EmployeesController に、更新画面表示用のActionを追加します。
[HttpGet]public IActionResult Edit(int id){ EmployeeRepository repository = new EmployeeRepository();
EmployeeEditModel? employee = repository.FindEditById(id);
if (employee == null) { return NotFound(); }
return View(employee);}POST用Edit Actionを追加する
Section titled “POST用Edit Actionを追加する”[HttpPost][ValidateAntiForgeryToken]public IActionResult Edit(EmployeeEditModel model){ ValidateEmployeeEditModel(model);
if (!ModelState.IsValid) { return View(model); }
try { EmployeeRepository repository = new EmployeeRepository();
int count = repository.Update(model);
if (count == 0) { ModelState.AddModelError("", "更新対象が見つかりません。研修用ID 9000番台のみ更新できます。"); return View(model); }
TempData["Message"] = "社員情報を更新しました。";
return RedirectToAction(nameof(Index)); } catch (OracleException ex) { ModelState.AddModelError("", "Oracle Database処理中にエラーが発生しました。"); ModelState.AddModelError("", ex.Message);
return View(model); }}Edit.cshtmlを作成する
Section titled “Edit.cshtmlを作成する”Views/Employees フォルダに Edit.cshtml を作成します。
Create.cshtml とほぼ同じですが、社員IDは変更させないようにします。
@model Chapter20_MvcCrud.Models.EmployeeEditModel
@{ ViewData["Title"] = "社員情報更新";}
<h1>社員情報更新</h1>
<div asp-validation-summary="All" class="text-danger"></div>
<form asp-controller="Employees" asp-action="Edit" method="post"> <input type="hidden" asp-for="EmployeeId" />
<div class="mb-3"> <label class="form-label">社員ID</label> <input value="@Model.EmployeeId" class="form-control" readonly /> </div>
<div class="mb-3"> <label asp-for="EmployeeName" class="form-label">社員名</label> <input asp-for="EmployeeName" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Yomi" class="form-label">よみがな</label> <input asp-for="Yomi" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="JobId" class="form-label">職種ID</label> <input asp-for="JobId" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="ManagerId" class="form-label">上司ID</label> <input asp-for="ManagerId" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="HireDate" class="form-label">入社日</label> <input asp-for="HireDate" type="date" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Salary" class="form-label">給与</label> <input asp-for="Salary" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Commission" class="form-label">歩合</label> <input asp-for="Commission" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="DepartmentId" class="form-label">部署ID</label> <input asp-for="DepartmentId" class="form-control" /> </div>
<button type="submit" class="btn btn-primary">更新</button> <a asp-controller="Employees" asp-action="Index" class="btn btn-secondary">戻る</a></form>20-9 一覧画面に更新リンクを追加する
Section titled “20-9 一覧画面に更新リンクを追加する”Index.cshtml の一覧表に更新リンクを追加します。
<td> <a asp-controller="Employees" asp-action="Details" asp-route-id="@employee.EmployeeId">詳細</a> | <a asp-controller="Employees" asp-action="Edit" asp-route-id="@employee.EmployeeId">更新</a></td>ただし、この章では9000番台のみ更新対象です。
既存データの更新リンクを押した場合、画面は表示されても、実際の更新時にエラー扱いになります。
発展として、9000番台のみ更新リンクを表示するようにしてもよいです。
@if (employee.EmployeeId >= 9000){ <a asp-controller="Employees" asp-action="Edit" asp-route-id="@employee.EmployeeId">更新</a>}20-10 削除処理を作成する
Section titled “20-10 削除処理を作成する”RepositoryにDeleteを追加する
Section titled “RepositoryにDeleteを追加する”EmployeeRepository に、削除用メソッドを追加します。
public int Delete(int employeeId){ string sql = @" DELETE FROM employees WHERE employee_id = :employeeId AND employee_id >= 9000";
using (OracleConnection connection = new OracleConnection(_connectionString)) { connection.Open();
using (OracleCommand command = new OracleCommand(sql, connection)) { command.BindByName = true; command.Parameters.Add("employeeId", OracleDbType.Int32).Value = employeeId;
return command.ExecuteNonQuery(); } }}このメソッドも、9000番台の社員だけを削除対象にしています。
Delete確認画面を表示する
Section titled “Delete確認画面を表示する”EmployeesController に、削除確認画面を表示するActionを追加します。
[HttpGet]public IActionResult Delete(int id){ EmployeeRepository repository = new EmployeeRepository();
EmployeeDetail? employee = repository.FindDetailById(id);
if (employee == null) { return NotFound(); }
return View(employee);}削除実行Action
Section titled “削除実行Action”POSTで削除を実行します。
[HttpPost][ValidateAntiForgeryToken][ActionName("Delete")]public IActionResult DeleteConfirmed(int id){ try { EmployeeRepository repository = new EmployeeRepository();
int count = repository.Delete(id);
if (count == 0) { TempData["Message"] = "削除対象が見つかりません。研修用ID 9000番台のみ削除できます。"; } else { TempData["Message"] = "社員情報を削除しました。"; }
return RedirectToAction(nameof(Index)); } catch (OracleException ex) { TempData["Message"] = "Oracle Database処理中にエラーが発生しました。" + ex.Message;
return RedirectToAction(nameof(Index)); }}Delete.cshtmlを作成する
Section titled “Delete.cshtmlを作成する”Views/Employees フォルダに Delete.cshtml を作成します。
@model Chapter20_MvcCrud.Models.EmployeeDetail
@{ ViewData["Title"] = "社員削除";}
<h1>社員削除</h1>
<div class="alert alert-warning"> 次の社員を削除します。よろしいですか?</div>
<table class="table"> <tr> <th>社員ID</th> <td>@Model.EmployeeId</td> </tr> <tr> <th>社員名</th> <td>@Model.EmployeeName</td> </tr> <tr> <th>部署名</th> <td>@Model.DepartmentName</td> </tr></table>
@if (Model.EmployeeId < 9000){ <div class="alert alert-danger"> 既存のSQL研修データは削除できません。削除できるのは9000番台の研修用データのみです。 </div>}else{ <form asp-controller="Employees" asp-action="Delete" method="post"> <input type="hidden" name="id" value="@Model.EmployeeId" /> <button type="submit" class="btn btn-danger">削除</button> <a asp-controller="Employees" asp-action="Index" class="btn btn-secondary">戻る</a> </form>}20-11 一覧画面に削除リンクを追加する
Section titled “20-11 一覧画面に削除リンクを追加する”Index.cshtml の一覧表に削除リンクを追加します。
<td> <a asp-controller="Employees" asp-action="Details" asp-route-id="@employee.EmployeeId">詳細</a>
@if (employee.EmployeeId >= 9000) { @: | <a asp-controller="Employees" asp-action="Edit" asp-route-id="@employee.EmployeeId">更新</a>
@: | <a asp-controller="Employees" asp-action="Delete" asp-route-id="@employee.EmployeeId">削除</a> }</td>このようにすると、9000番台の研修用データだけに更新・削除リンクを表示できます。
20-12 処理の流れを整理する
Section titled “20-12 処理の流れを整理する”1. 一覧画面で「新規登録」をクリックする2. GET /Employees/Create が呼ばれる3. Create.cshtml が表示される4. 入力して登録ボタンを押す5. POST /Employees/Create が呼ばれる6. Controllerが入力値を受け取る7. 入力チェックを行う8. Repository.Insert が呼ばれる9. INSERT文が実行される10. 一覧画面へリダイレクトする1. 一覧画面で「更新」をクリックする2. GET /Employees/Edit/9001 が呼ばれる3. Repository.FindEditById で既存データを取得する4. Edit.cshtml に既存値が表示される5. 修正して更新ボタンを押す6. POST /Employees/Edit が呼ばれる7. Repository.Update が呼ばれる8. UPDATE文が実行される9. 一覧画面へリダイレクトする1. 一覧画面で「削除」をクリックする2. GET /Employees/Delete/9001 が呼ばれる3. Delete.cshtml で削除確認する4. 削除ボタンを押す5. POST /Employees/Delete が呼ばれる6. Repository.Delete が呼ばれる7. DELETE文が実行される8. 一覧画面へリダイレクトするよくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
| 登録ボタンを押してもActionに入らない | formの asp-action が違う | Viewのformタグを確認する |
| 入力値がModelに入らない | asp-for やプロパティ名が違う | ModelとViewの対応を確認する |
| INSERTで主キーエラーになる | 既に存在する社員IDを指定している | 9000番台で未使用のIDを使う |
| INSERTで外部キーエラーになる | 存在しない部署IDを指定している | 部署IDは1〜5を使う |
| UPDATEしても変わらない | employee_id >= 9000 の条件に合っていない | 9000番台のデータだけ更新する |
| DELETEしても消えない | 9000番台以外を削除しようとしている | 研修用データだけ削除する |
| 日付入力でエラーになる | 日付形式が合っていない | type="date" を使う |
| NULLを登録できない | DBNull.Value を渡していない | 空欄の場合は DBNull.Value にする |
| 更新後に再送信確認が出る | POST後にViewを直接返している | 成功時は RedirectToAction を使う |
学んだことチェック
Section titled “学んだことチェック”- CRUDの意味を説明できる
- GETとPOSTの使い分けを説明できる
- 登録画面を表示できる
- POSTされた入力値をModelで受け取れる
- RepositoryでINSERTを実行できる
- RepositoryでUPDATEを実行できる
- RepositoryでDELETEを実行できる
-
ExecuteNonQueryの役割を説明できる -
ModelStateを使って入力エラーを表示できる -
RedirectToActionで一覧画面へ戻れる -
TempDataで完了メッセージを表示できる - 更新・削除を9000番台に限定する理由を説明できる
この章の演習課題に取り組みます。
制限時間は 100分 です。
時間内にすべて完成しなくても構いません。
できたところまでを保存し、Gitに提出してください。
課題20-1 EmployeeEditModelを作成する
Section titled “課題20-1 EmployeeEditModelを作成する”登録・更新用の EmployeeEditModel を作成してください。
条件:
Modelsフォルダに作成するEmployeeId、EmployeeName、Yomi、JobId、ManagerId、HireDate、Salary、Commission、DepartmentIdを持つ- NULLになる可能性がある項目には
?を付ける
課題20-2 登録画面を作成する
Section titled “課題20-2 登録画面を作成する”社員登録画面を作成してください。
条件:
CreateActionを作成するCreate.cshtmlを作成するEmployeeEditModelをViewに渡す- 社員ID、社員名、部署IDなどを入力できる
課題20-3 INSERT処理を作成する
Section titled “課題20-3 INSERT処理を作成する”EmployeeRepository に Insert メソッドを作成してください。
条件:
INSERT INTO employeesを使う- SQLパラメーターを使う
ExecuteNonQueryを使う- NULLにする項目は
DBNull.Valueを渡す
課題20-4 登録後に一覧画面へ戻る
Section titled “課題20-4 登録後に一覧画面へ戻る”登録処理が成功したら、一覧画面へ戻してください。
条件:
- POST用
CreateActionを作成する repository.Insert(model)を呼び出す- 成功時は
RedirectToAction(nameof(Index))を使う TempDataで登録完了メッセージを表示する
課題20-5 更新画面を作成する
Section titled “課題20-5 更新画面を作成する”社員情報を更新できる画面を作成してください。
条件:
Edit(int id)Actionを作成するFindEditByIdで既存データを取得するEdit.cshtmlを作成する- 既存値をフォームに表示する
課題20-6 UPDATE処理を作成する
Section titled “課題20-6 UPDATE処理を作成する”EmployeeRepository に Update メソッドを作成してください。
条件:
UPDATE employeesを使う- SQLパラメーターを使う
employee_id >= 9000の条件を入れる- 更新件数を戻り値として返す
課題20-7 削除確認画面を作成する
Section titled “課題20-7 削除確認画面を作成する”削除前に確認画面を表示してください。
条件:
Delete(int id)Actionを作成するDelete.cshtmlを作成する- 社員ID、社員名、部署名を表示する
- 9000番台以外は削除ボタンを表示しない
課題20-8 DELETE処理を作成する
Section titled “課題20-8 DELETE処理を作成する”EmployeeRepository に Delete メソッドを作成してください。
条件:
DELETE FROM employeesを使う- SQLパラメーターを使う
employee_id >= 9000の条件を入れる- 削除件数を戻り値として返す
課題20-9 一覧画面に操作リンクを追加する
Section titled “課題20-9 一覧画面に操作リンクを追加する”一覧画面に、登録・更新・削除へのリンクを追加してください。
条件:
- 「新規登録」ボタンを追加する
- 9000番台のデータにだけ「更新」「削除」を表示する
- 詳細リンクはこれまで通り表示する
課題20-10 入力チェックを追加する
Section titled “課題20-10 入力チェックを追加する”登録・更新時に簡単な入力チェックを追加してください。
条件:
- 社員IDは9000〜9999
- 社員名は必須
- 部署IDは1〜5
- 給与と歩合は0以上
- エラーがある場合は登録・更新しない
Gitへの提出
Section titled “Gitへの提出”課題が終わったら、できたところまでをGitに提出します。
git statusgit add .git commit -m "Chapter20 MVC CRUD処理"git pushGitの操作でエラーが出た場合は、自己判断で同じ操作を繰り返さず、講師に確認してください。
提出前チェックリスト
Section titled “提出前チェックリスト”-
EmployeeEditModelを作成している - 登録画面を作成している
- 登録処理でINSERTを実行している
- 更新画面を作成している
- 更新処理でUPDATEを実行している
- 削除確認画面を作成している
- 削除処理でDELETEを実行している
- SQLパラメーターを使っている
- NULL項目に
DBNull.Valueを渡している - 9000番台だけ更新・削除できるようにしている
- エラー時に画面へ戻れる
- 成功時に一覧画面へ戻れる
- Gitにcommitしている
- Gitにpushしている
この章のまとめ
Section titled “この章のまとめ”この章では、ASP.NET Core MVCアプリからOracle Databaseに対して、登録・更新・削除を行う基本的な流れを学習しました。
この章で学んだ主な内容は次の通りです。
- CRUDは、Create、Read、Update、Deleteの4つの基本操作である
- 登録・更新・削除では、主にPOSTを使う
- フォーム入力用のModelとして
EmployeeEditModelを作成した asp-forを使うと、Modelのプロパティと入力欄を対応させられるINSERT、UPDATE、DELETEではExecuteNonQueryを使う- SQLに値を渡すときはSQLパラメーターを使う
- NULLとして登録・更新したい値には
DBNull.Valueを渡す ModelStateを使って入力エラーを表示できる- 処理成功後は
RedirectToActionで一覧画面へ戻る TempDataを使うと、リダイレクト後にメッセージを表示できる- 研修では、既存データを守るために9000番台の社員IDだけ更新・削除対象にした
次章からは、デスクトップアプリ編 に入ります。
Windowsフォームアプリを作成し、ボタン、テキストボックス、DataGridViewなどを使いながら、デスクトップアプリの基本構成を体験します。