付録L 復習:MVC で作る簡単計算アプリ
この付録の目的
Section titled “この付録の目的”第 26 章で ASP.NET Core MVC(占いアプリ)を学びました。 ただ、Web の仕組み(リクエスト/レスポンス、Model・View・Controller、Model Binding)は一度では身につきにくく、週末をはさむと記憶が薄れがち です。
そこで本付録では、もっと単純な題材 ── 第 19 章で Windows フォームとして作った 簡単計算プログラム(SimpleCalc) を、今度は MVC で作り直し ながら、第 26 章で学んだことを 手を動かして復習 します。
占いアプリと同じ骨組みです
「入力画面 → 送信 → 結果画面」という 2 画面構成は、第 26 章の占いアプリとまったく同じです。 違うのは、結果がランダムな占いではなく 2 つの数の足し算 になるだけ。仕組みの確認に集中できます。
この付録には、新しく 1 つだけ ポイントを足します。
- 前半:入力された値を そのまま(単純な値として)取り出して 計算する
- 後半:計算オブジェクト(Model クラス) を作り、そのオブジェクトで値を受け取り、計算メソッドを呼び出し、計算式(
3 + 5 = 8) を画面に表示する
これは第 26 章 26-10 で触れた 「Model Binding は単純な型でも、クラス型でも値を受け取れる」 を、実際に 両方書いて比べる 復習です。
第 26 章の説明は繰り返しません
asp-*や@model、GET / POST の流れなどは、第 26 章を参照 しながら進めてください(本付録では「どこを見ればよいか」を案内します)。思い出せないところは、その都度 26 章に戻るのが効率的です。
この付録でできるようになること
Section titled “この付録でできるようになること”- MVC の基本フロー(GET で入力画面 → POST で結果画面)を、計算アプリで再現できる
- 単純な型の引数(
int left, int right)で入力値を受け取れる(単純型の Model Binding) - 計算ロジックを持つ Model クラス を作り、クラス型 で入力値を受け取れる
- 単純型とクラス型の 受け取り方の違い を自分の言葉で説明できる
- Windows フォーム版(第 19 章)と Web 版の 対応 を説明できる
本付録で使用する環境
Section titled “本付録で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | ASP.NET Core Web アプリ(Model-View-Controller) |
| 対象フレームワーク | .NET 8 |
| ソリューション名 | ChapterL |
| プロジェクト名 | ChL_MvcSimpleCalc |
| 認証の種類 | なし |
| データベース | 使わない |
前提:第 26 章を一度終えていること
本付録は第 26 章の復習です。プロジェクトの作り方・Web アプリの動かし方(F5・Kestrel・停止)は 第 26 章 26-3 / 26-4 を参照してください。 csproj は今回も
<Nullable>disable</Nullable>に変更します(第 1 章「1-1」参照)。
L-1 何を作るか(第 19 章の計算アプリを Web で)
Section titled “L-1 何を作るか(第 19 章の計算アプリを Web で)”第 19 章では、Windows フォームで次のような計算プログラムを作りました。
[Windows フォーム版(第 19 章 SimpleCalc)]
1つ目の数: [ 3 ] 2つ目の数: [ 5 ] [ 計算 ] 答え: 8本付録では、これを Web アプリ にします。画面は 2 つに分かれます。
[Web 版・入力画面 /Calc] [Web 版・結果画面 /Calc/Result]
1つ目の数: [ 3 ] 答え: 8 2つ目の数: [ 5 ] ──→ (後半では「3 + 5 = 8」と式で表示) [ 計算する ] [ もう一度 ]第 26 章の占いアプリと 同じ 2 画面構成 です。占いの「名前・生年月日・気分」が「2 つの数」に、「ラッキーカラー」が「足し算の答え」に変わっただけ、と捉えてください。
L-2 プロジェクトを作る
Section titled “L-2 プロジェクトを作る”第 26 章 26-3 と同じ手順で、MVC プロジェクトを作ります。
| 項目 | 値 |
|---|---|
| テンプレート | ASP.NET Core Web アプリ(Model-View-Controller) |
| ソリューション名 | ChapterL |
| プロジェクト名 | ChL_MvcSimpleCalc |
| フレームワーク | .NET 8.0 |
| 認証の種類 | なし |
作成後、ChL_MvcSimpleCalc.csproj を開いて <Nullable>disable</Nullable> に変更します。
F5 でテンプレートの初期画面が出ることを確認したら、準備完了です。
〔前半〕単純な値の取り出しで作る
Section titled “〔前半〕単純な値の取り出しで作る”まずは Model クラスを使わず、入力された値を そのまま 受け取って計算します。 ここでは「入力画面を出す → 値を受け取って足す」という MVC の往復を、いちばん素朴な形で確認します。
L-3 Step1:入力画面を表示する
Section titled “L-3 Step1:入力画面を表示する”入力画面を担当する Controller と View を作ります。
Controller を作る:CalcController(まずは Index だけ)
Section titled “Controller を作る:CalcController(まずは Index だけ)”Controllers フォルダに、右クリック → 追加 → コントローラー → 「MVC コントローラー - 空」 で CalcController.cs を作ります。
using Microsoft.AspNetCore.Mvc;
namespace ChL_MvcSimpleCalc.Controllers;
public class CalcController : Controller{ [HttpGet] public IActionResult Index() { return View(); }}[HttpGet] Index()は、/Calcまたは/Calc/Indexを開いたときに呼ばれ、入力画面を返します。- 今回は入力画面に渡すデータが無いので、
return View()(Model なし)です(View()とView(model)の違いは第 26 章 26-10 の補足を参照)。
入力 View を作る:Views/Calc/Index.cshtml
Section titled “入力 View を作る:Views/Calc/Index.cshtml”Views フォルダの中に Calc フォルダを作り、その中に Razor ビュー - 空 で Index.cshtml を作ります。
@{ ViewData["Title"] = "かんたん計算";}
<h1>かんたん計算(足し算)</h1>
<form asp-controller="Calc" asp-action="Result" method="post"> <div class="mb-3"> <label for="left" class="form-label">1つ目の数</label> <input type="number" id="left" name="left" class="form-control" /> </div>
<div class="mb-3"> <label for="right" class="form-label">2つ目の数</label> <input type="number" id="right" name="right" class="form-control" /> </div>
<button type="submit" class="btn btn-primary">計算する</button></form>ポイント(詳しくは第 26 章 26-9・26-12 早見表):
<form ... asp-action="Result" method="post">:送信するとCalcコントローラーのResultアクションへ POST されます。<input name="left">/<input name="right">:このnameがカギ です。送信時にleft/rightという名前で値が運ばれます(次の Step で、この名前が引数に対応します)。type="number":数字用の入力欄になります。
ここで動かす(Step1)
F5 で実行し、
/Calcを開いてください。2 つの入力欄と「計算する」ボタンが表示されれば成功 です。 ※この時点で「計算する」を押すと、まだResultを作っていないのでエラーになります(次の Step で作ります)。
L-4 Step2:値を「そのまま」受け取って計算する
Section titled “L-4 Step2:値を「そのまま」受け取って計算する”送信された 2 つの値を受け取り、足し算して結果画面に表示します。 ここが前半のキモ ── 値を クラスにまとめず、単純な引数のまま 受け取ります。
Controller に Result(POST)を足す
Section titled “Controller に Result(POST)を足す”CalcController に、Result メソッドを足します。
using Microsoft.AspNetCore.Mvc;
namespace ChL_MvcSimpleCalc.Controllers;
public class CalcController : Controller{ [HttpGet] public IActionResult Index() { return View(); }
[HttpPost] public IActionResult Result(int left, int right) { int answer = left + right; ViewBag.Answer = answer; return View(); }}ここがいちばん大事なところです。
Result(int left, int right)── アクションの引数にint left, int rightと書くだけで、フォームのname="left"/name="right"の値が 自動的に入って きます。これが Model Binding(第 26 章 26-10)です。今回は クラスを使わず、値を 1 個ずつ単純な型(int)で受け取って います。- 受け取った値はただの
intなので、left + rightで そのまま足せます。 - 結果は Model にまとめず、
ViewBagで View に渡しています(ViewBagは「Model 以外の値をちょっと渡す」しくみ。第 26 章 課題 26-1 の補足を参照)。
第 19 章とのつながり:変換は誰がやる?
第 19 章の Windows 版では、
int.TryParse(input1TextBox.Text, out int a)のように、文字列を自分でintに変換 していました。 Web の MVC では、引数をint leftと書いておくと、Model Binding が文字列 →intの変換を代わりにやって くれます。「Forms で手書きしていた変換を、MVC では枠組みが肩代わりする」と捉えると、両者がつながります。 (数字でない値が送られた場合は0として扱われます。きちんとした入力チェックは第 30 章のテーマなので、ここでは深入りしません。)
結果 View を作る:Views/Calc/Result.cshtml
Section titled “結果 View を作る:Views/Calc/Result.cshtml”Views/Calc フォルダに、Result.cshtml を Razor ビュー - 空 で作ります。
@{ ViewData["Title"] = "計算結果";}
<h1>計算結果</h1>
<div class="alert alert-info"> <p>答え: <strong>@ViewBag.Answer</strong></p></div>
<p> <a asp-controller="Calc" asp-action="Index" class="btn btn-secondary">もう一度</a></p>@ViewBag.Answerで、Controller がセットした計算結果がそのまま表示されます。- 「もう一度」は入力画面へ戻る GET リンクです。
ここで動かす(Step2・前半完成)
F5 →
/Calcで「3」と「5」を入れて「計算する」。結果画面に「答え: 8」が出れば前半は成功 です。 入力した値がResult(int left, int right)に届き(Model Binding)、足し算されて表示される ── この往復が、第 26 章の占いアプリと同じ流れであることを確認してください。
〔後半〕計算オブジェクトを作って計算する
Section titled “〔後半〕計算オブジェクトを作って計算する”前半では、値を int left, int right と バラバラのまま 受け取りました。
後半では、2 つの値と計算する力を 1 つにまとめた「計算オブジェクト」(Model クラス) を作ります。
これは第 7〜10 章で学んだ 「データと振る舞いを持つクラス」 の復習でもあります。
L-5 Step3:計算オブジェクト(Model)を作る
Section titled “L-5 Step3:計算オブジェクト(Model)を作る”Models フォルダに、右クリック → 追加 → クラス で CalcModel.cs を作ります。
namespace ChL_MvcSimpleCalc.Models;
public class CalcModel{ // 入力された 2 つの数 public int Left { get; set; } public int Right { get; set; }
// 計算結果 public int Answer { get; set; }
// 計算メソッド:2 つの数を足して Answer に入れる public void Calculate() { Answer = Left + Right; }
// 計算式の文字列を作る("3 + 5 = 8" のような形) public string Formula() { return $"{Left} + {Right} = {Answer}"; }}このクラスは、データ(Left・Right・Answer)と、振る舞い(Calculate・Formula)を 1 つにまとめた オブジェクトです。
| メンバー | 役割 |
|---|---|
Left / Right | 入力された 2 つの数(フォームから受け取る) |
Answer | 計算結果を入れておく場所 |
Calculate() | 計算メソッド。Left + Right を Answer に入れる |
Formula() | 計算式の文字列("3 + 5 = 8")を組み立てて返す |
前半との違いはここ
前半は値を足すだけで、計算は Controller の中で
left + rightと書いていました。 後半は、計算する力をオブジェクト自身が持っています(Calculateメソッド)。Controller は「オブジェクトに計算を頼む」だけになります。
L-6 Step4:オブジェクトで受け取り、計算して、式を表示する
Section titled “L-6 Step4:オブジェクトで受け取り、計算して、式を表示する”Controller の Result をクラス型に書き換える
Section titled “Controller の Result をクラス型に書き換える”Result の引数を、int left, int right から CalcModel model に変えます。
using Microsoft.AspNetCore.Mvc;using ChL_MvcSimpleCalc.Models;
namespace ChL_MvcSimpleCalc.Controllers;
public class CalcController : Controller{ [HttpGet] public IActionResult Index() { return View(); }
[HttpPost] public IActionResult Result(CalcModel model) { // model.Left / model.Right は Model Binding で自動的に入っている model.Calculate(); // 計算メソッドを呼び出す return View(model); // 計算済みの model を View に渡す }}前半と見比べてください。変わったのは受け取り方だけ です。
- 前半:
Result(int left, int right)── 値を 1 個ずつ受け取る(単純型) - 後半:
Result(CalcModel model)──Left/Rightを まとめて 1 つのオブジェクトで受け取る(クラス型)
クラス型で受け取ると、フォームの name="Left" / name="Right" が、model.Left / model.Right に 自動でマッピング されます(第 26 章 26-10 の FortuneViewModel と同じしくみです)。
あとは model.Calculate() で 計算メソッドを呼び出し、計算済みの model を View に渡すだけです。
入力 View を asp-for に書き換える
Section titled “入力 View を asp-for に書き換える”クラス型で受け取るので、入力 View も CalcModel に合わせます。Views/Calc/Index.cshtml を次のようにします。
@model ChL_MvcSimpleCalc.Models.CalcModel
@{ ViewData["Title"] = "かんたん計算";}
<h1>かんたん計算(足し算)</h1>
<form asp-controller="Calc" asp-action="Result" method="post"> <div class="mb-3"> <label asp-for="Left" class="form-label">1つ目の数</label> <input asp-for="Left" type="number" class="form-control" /> </div>
<div class="mb-3"> <label asp-for="Right" class="form-label">2つ目の数</label> <input asp-for="Right" type="number" class="form-control" /> </div>
<button type="submit" class="btn btn-primary">計算する</button></form>@model ... CalcModelで、この View がCalcModelを扱うと宣言します。asp-for="Left"は、前半で手書きしたname="left"をCalcModel.Leftから自動で作って くれます(asp-forが何に変わるかは第 26 章 26-9 を参照)。プロパティ名と同じname="Left"が生成されるので、送信時にmodel.Leftに届きます。
結果 View で計算式を表示する
Section titled “結果 View で計算式を表示する”Views/Calc/Result.cshtml を、CalcModel を受け取って 計算式 を表示する形にします。
@model ChL_MvcSimpleCalc.Models.CalcModel
@{ ViewData["Title"] = "計算結果";}
<h1>計算結果</h1>
<div class="alert alert-info"> <p class="fs-4">@Model.Formula()</p></div>
<p> <a asp-controller="Calc" asp-action="Index" class="btn btn-secondary">もう一度</a></p>@Model.Formula()は、オブジェクトが組み立てた計算式 ── たとえば3 + 5 = 8── をそのまま表示します。- 前半は「答え: 8」と 結果だけ でしたが、後半はオブジェクトが 計算式まで 作って見せてくれます。
ここで動かす(Step4・後半完成)
F5 →
/Calcで「3」「5」を入れて「計算する」。結果画面に「3 + 5 = 8」と式で表示されれば後半は成功 です。 入力 →CalcModelにまとまって届く →Calculate()で計算 →Formula()で式を表示、という流れを目で追ってみましょう。
L-7 前半と後半のちがい(単純型 vs クラス型)
Section titled “L-7 前半と後半のちがい(単純型 vs クラス型)”同じ計算アプリを、2 通りの受け取り方で作りました。並べて確認します。
| 観点 | 前半(単純型) | 後半(クラス型) |
|---|---|---|
| Controller の受け取り | Result(int left, int right) | Result(CalcModel model) |
| 値の置き場所 | バラバラの引数 | オブジェクト(model.Left など)にまとまる |
| 計算する場所 | Controller の中(left + right) | オブジェクトの計算メソッド(model.Calculate()) |
| View へ渡す | ViewBag | return View(model) |
| 画面表示 | 答えだけ(8) | 計算式(3 + 5 = 8) |
| 入力欄の書き方 | 手書きの name="left" | asp-for="Left"(自動で name 生成) |
流れを図で見る
Section titled “流れを図で見る”「計算する」を押してから結果が表示されるまでの流れを、前半・後半で並べてみます(GET / POST の全体像そのものは第 26 章 26-13 のシーケンス図も参照)。
前半(単純型):計算は Controller の中で行う
後半(クラス型):計算はオブジェクト(CalcModel)に頼む
前半は Controller の中で left + right を計算、後半は CalcModel に計算を頼み(Calculate())、式まで作ってもらう(Formula()) ── 同じ足し算でも「誰が計算するか」が違います。これが単純型とクラス型の差です。
第 26 章 26-10 の補足で 「Model Binding は単純な型でも、クラス型でも値を受け取れる」 と学びました。本付録では、その 両方を実際に書いて みたわけです。
- 値が 少なく、計算も単純 なら、前半のように 引数で受け取る のが手軽。
- 値が 増えたり、データに振る舞い(計算メソッドなど)を持たせたい なら、後半のように クラス(オブジェクト)にまとめる のが見通しよくなります。
第 26 章の FortuneViewModel、第 28〜30 章の Employee なども、すべて後半と同じ 「クラス型で受け取る」 やり方です。
L-8 Windows フォーム版(第 19 章)との対応
Section titled “L-8 Windows フォーム版(第 19 章)との対応”第 19 章の SimpleCalc(Windows フォーム)と、本付録(Web)を並べてみます。
| 観点 | Windows フォーム(第 19 章) | Web MVC(本付録) |
|---|---|---|
| 入力部品 | TextBox(input1TextBox など) | <input type="number"> |
| 実行のきっかけ | ボタンの Click イベント | フォームの POST 送信 |
| 入力値の取り出し | input1TextBox.Text を int.TryParse | 引数 int left / model.Left(Model Binding) |
| 計算 | valueLeft + valueRight | left + right / model.Calculate() |
| 結果の表示 | answerTextBox.Text に代入 | View に渡して HTML で表示 |
「入力 → 計算 → 表示」という流れは、Windows でも Web でも同じです。
違うのは、値の取り出し方(.Text か Model Binding か) と、結果の見せ方(コントロールか HTML か) だけ ── これは第 26 章 26-15 で見た「占いアプリ vs SimpleCalc」と同じ対比です。
学んだことチェック
Section titled “学んだことチェック”- MVC の入力画面(GET)→ 結果画面(POST)の往復を、計算アプリで再現できた
-
Result(int left, int right)で、値を 単純な型のまま 受け取れることを説明できる -
Result(CalcModel model)で、値を オブジェクトにまとめて 受け取れることを説明できる -
CalcModelが データ(Left/Right)と振る舞い(Calculate/Formula) を持つことを説明できる - 計算メソッド
Calculate()を呼び、Formula()で計算式を表示する流れを説明できる - 単純型とクラス型の Model Binding の使い分け(値が少ない/多い・振る舞いの有無)を説明できる
- Windows 版(第 19 章)と Web 版で、「入力 → 計算 → 表示」の流れが共通だと説明できる
次の内容を、自分の言葉で説明してください。
- 前半の
Result(int left, int right)では、フォームの値はどうやって引数に入りますか。 - 後半の
Result(CalcModel model)では、model.Leftには何が入っていますか。なぜ入るのですか。 CalcModelのCalculate()とFormula()は、それぞれ何をするメソッドですか。- 前半(単純型)と後半(クラス型)は、どんなときにどちらを選ぶとよいですか。
- 第 19 章の Windows 版で
int.TryParseがやっていた仕事は、MVC では誰がやっていますか。
発展(余裕があれば)
Section titled “発展(余裕があれば)”復習が終わって余裕があれば、次のどれかを試してみましょう(任意・提出不要)。
| 選択肢 | 内容 | ヒント |
|---|---|---|
| A:引き算も作る | 答えに加えて「差」も表示する | CalcModel に Subtract() を足し、Formula() を増やす |
| B:四則すべて | 足す・引く・かける・割るを選べるようにする | 入力画面に <select>(演算子)を足し、第 26 章占いの気分プルダウンを参考に |
| C:計算履歴 | これまでの計算式を一覧で出す(同じ画面内) | static List<string> に式をためる。Random.Shared のように共有する点に注意 |
割り算(B)を入れるときは、0 で割るとエラー になります。
Right == 0のときの扱いを考えてみると、入力チェック(第 30 章)の予習になります。次の発展 D は、その「0 で割るとき」を題材にします。
発展 D:条件によって表示する画面を切り替える(return View("名前"))
Section titled “発展 D:条件によって表示する画面を切り替える(return View("名前"))”ここまで Controller は、いつも アクション名と同じ View(Result アクション → Result.cshtml)を返してきました。
この発展では、条件によって表示する画面そのものを切り替えます。題材は「割り算で 0 で割ろうとしたとき」です。
やること
- 割り算ができるようにする(発展 B で作っていればそれを使う。まだなら
CalcModelに割り算を 1 つ足すだけでも試せます) - 2 つ目の数が
0のとき → 専用の エラー画面(Error.cshtml)を表示する - それ以外 → これまでどおり 結果画面(
Result.cshtml)を表示する
Controller のイメージ
[HttpPost]public IActionResult Result(CalcModel model){ if (model.Right == 0) { return View("Error", model); // ← View 名を明示して、別の画面を表示 }
model.Calculate(); return View(model); // ← これまでどおり Result.cshtml(= View("Result", model) と同じ)}エラー画面 Views/Calc/Error.cshtml のイメージ
@model ChL_MvcSimpleCalc.Models.CalcModel
@{ ViewData["Title"] = "エラー";}
<h1>計算できません</h1>
<div class="alert alert-danger"> <p>0 で割ることはできません。</p></div>
<p> <a asp-controller="Calc" asp-action="Index" class="btn btn-secondary">もう一度</a></p>ここがこの発展のポイントです。
return View("Error", model)… アクション名(Result)とは 違う名前の View(Error.cshtml)を、名前で指定 して表示します。return View(model)… これまでどおり、アクション名と同じResult.cshtmlを表示します(return View("Result", model)と同じ意味)。- つまり 同じアクションの中でも、条件によって表示する画面を選び分けられます。「1 アクション = 1 画面」に固定ではありません。
- なお、
Right == 0のチェックを 計算する前 に置くことで、0で割って プログラムが落ちる(例外) ことも同時に防いでいます。
これは第 30 章 PRG と同じ考え方です
第 30 章の保存処理では、
if (!ModelState.IsValid) return View(model);(入力エラーなら入力画面に戻す)/ 成功ならreturn RedirectToAction("Index")(一覧へ)と、条件で行き先を変えて いました。 View 名を明示するreturn View("Error", model)も、同じ「条件によって見せる画面を変える」発想です。これが分かると、第 30 章の Controller がぐっと読みやすくなります。
確認すること
- 2 つ目に
0を入れて割ると、エラー画面(Error.cshtml)が出る - それ以外の計算は、これまでどおり結果画面(
Result.cshtml)が出る -
return View("Error", model)とreturn View(model)の違い(View 名を明示するか・しないか)を説明できる - 「条件で表示する画面を切り替える」のは、第 30 章 PRG と同じ考え方だと気づけた
この付録のまとめ
Section titled “この付録のまとめ”- 本付録は、第 19 章の計算アプリ(
SimpleCalc)を MVC で作り直す 第 26 章の復習 - 入力画面(GET)→ 結果画面(POST)の 2 画面構成 は、占いアプリと同じ骨組み
- 前半:値を
int left, int rightと 単純な型のまま 受け取って計算(Model クラスなし・ViewBagで表示) - 後半:
CalcModel(データ + 計算メソッド)を作り、クラス型 で受け取り、Calculate()を呼んでFormula()で 計算式を表示 - 同じ Model Binding が、単純型でもクラス型でも値を運んでくれる(第 26 章 26-10 の実地確認)
- Windows 版(第 19 章)と Web 版は「入力 → 計算 → 表示」が共通。違うのは値の取り出し方と表示の仕方だけ
ここまでスッと作れたら、第 26 章の内容はしっかり定着しています。 あいまいだったところは、26 章の該当節(26-9 / 26-10 / 26-12 / 26-13)に戻って確認しておきましょう。