Skip to content

付録L 復習:MVC で作る簡単計算アプリ

第 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 版の 対応 を説明できる

項目内容
開発環境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 つの数」に、「ラッキーカラー」が「足し算の答え」に変わっただけ、と捉えてください。


第 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 の往復を、いちばん素朴な形で確認します。


入力画面を担当する Controller と View を作ります。

Controller を作る:CalcController(まずは Index だけ)

Section titled “Controller を作る:CalcController(まずは Index だけ)”

Controllers フォルダに、右クリック → 追加 → コントローラー → 「MVC コントローラー - 空」CalcController.cs を作ります。

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 を作ります。

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 つの値を受け取り、足し算して結果画面に表示します。 ここが前半のキモ ── 値を クラスにまとめず、単純な引数のまま 受け取ります。

CalcController に、Result メソッドを足します。

CalcController.cs
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.cshtmlRazor ビュー - 空 で作ります。

Result.cshtml
@{
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 を作ります。

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}";
}
}

このクラスは、データ(LeftRightAnswer)と、振る舞い(CalculateFormula)を 1 つにまとめた オブジェクトです。

メンバー役割
Left / Right入力された 2 つの数(フォームから受け取る)
Answer計算結果を入れておく場所
Calculate()計算メソッドLeft + RightAnswer に入れる
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 に変えます。

CalcController.cs
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 を次のようにします。

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 に届きます。

Views/Calc/Result.cshtml を、CalcModel を受け取って 計算式 を表示する形にします。

Result.cshtml
@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 へ渡すViewBagreturn View(model)
画面表示答えだけ(8)計算式(3 + 5 = 8)
入力欄の書き方手書きの name="left"asp-for="Left"(自動で name 生成)

「計算する」を押してから結果が表示されるまでの流れを、前半・後半で並べてみます(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.Textint.TryParse引数 int left / model.Left(Model Binding)
計算valueLeft + valueRightleft + right / model.Calculate()
結果の表示answerTextBox.Text に代入View に渡して HTML で表示

入力 → 計算 → 表示」という流れは、Windows でも Web でも同じです。 違うのは、値の取り出し方(.Text か Model Binding か) と、結果の見せ方(コントロールか HTML か) だけ ── これは第 26 章 26-15 で見た「占いアプリ vs SimpleCalc」と同じ対比です。


  • MVC の入力画面(GET)→ 結果画面(POST)の往復を、計算アプリで再現できた
  • Result(int left, int right) で、値を 単純な型のまま 受け取れることを説明できる
  • Result(CalcModel model) で、値を オブジェクトにまとめて 受け取れることを説明できる
  • CalcModelデータ(Left/Right)と振る舞い(Calculate/Formula) を持つことを説明できる
  • 計算メソッド Calculate() を呼び、Formula() で計算式を表示する流れを説明できる
  • 単純型とクラス型の Model Binding の使い分け(値が少ない/多い・振る舞いの有無)を説明できる
  • Windows 版(第 19 章)と Web 版で、「入力 → 計算 → 表示」の流れが共通だと説明できる

次の内容を、自分の言葉で説明してください。

  1. 前半の Result(int left, int right) では、フォームの値はどうやって引数に入りますか。
  2. 後半の Result(CalcModel model) では、model.Left には何が入っていますか。なぜ入るのですか。
  3. CalcModelCalculate()Formula() は、それぞれ何をするメソッドですか。
  4. 前半(単純型)と後半(クラス型)は、どんなときにどちらを選ぶとよいですか。
  5. 第 19 章の Windows 版で int.TryParse がやっていた仕事は、MVC では誰がやっていますか。

復習が終わって余裕があれば、次のどれかを試してみましょう(任意・提出不要)。

選択肢内容ヒント
A:引き算も作る答えに加えて「差」も表示するCalcModelSubtract() を足し、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 のイメージ

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 と同じ考え方だと気づけた

  • 本付録は、第 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)に戻って確認しておきましょう。