付録F 発展課題集(本編を走り切った人向け)
この付録の位置づけ
Section titled “この付録の位置づけ”この付録は、本編(第 0〜31 章)を最後まで走り切った人 のために、もう一段深いテーマを集めたものです。
研修期間中に余裕のある人、早く終わった人、配属先で必要になりそうな技術を先取りしたい人が手を伸ばすための、任意の課題集 として用意しています。
本編との違い
Section titled “本編との違い”| 観点 | 本編(第 1〜31 章) | 本付録 F |
|---|---|---|
| 提出 | 必須 | 任意(やった人だけ提出) |
| 完全なコード掲載 | あり(本文に手順あり) | なし(仕様・ヒント・骨格コードのみ) |
| つまずいたとき | 本文に答えがある | 自分で調べる前提(StackOverflow、公式ドキュメント) |
| 質問の仕方 | 講師に「分かりません」で OK | 「ここまで調べました、ここで詰まっています」を整理してから |
本編は「正しい型を覚える」場でした。発展課題は「自分で型を選び取る」場です。やり方は 1 つではありません。動くものができたら、最初の答えとは違う方向に作り直してみてください。
F-1 取り組み方の原則
Section titled “F-1 取り組み方の原則”本付録には完全なコードを載せません。掲載しているのは:
- 仕様(何を作るか)
- ヒント(詰まりやすい箇所への指針)
- 骨格コード(メソッドシグネチャ、SQL の雛形、画面項目)
だけです。本編のように「コピペで動く」状態にはしていません。自分で組み立てる練習 のための場だと割り切ってください。
1 課題 = 1 ソリューション or 1 ブランチ
Section titled “1 課題 = 1 ソリューション or 1 ブランチ”複数の発展課題に手を出す場合、本編のソリューションを上書きせずに別物として残す ことを推奨します。 理由は本編と同じで、「前の到達点を消さない」ためです。
| やり方 | 例 |
|---|---|
| ソリューションを分ける | Kadai25 とは別に KadaiF_03_01_BulkSalaryUp を作る |
| Git ブランチを分ける | feature/appendix-f-bulksalary のように切る |
| プロジェクトを追加する | KadaiF ソリューションを 1 つ作り、各課題を KdF_XX_YY_* プロジェクトで追加 |
ファイル命名は本編に厳密に揃える必要はありません。自分が後で見直したとき判別できる こと、講師にレビューを頼むときに位置が指せる ことを優先してください。
「動くもの → 良いもの」の順
Section titled “「動くもの → 良いもの」の順”1 周目は 動かすこと を最優先にしてください。エラーハンドリングや見た目の調整は後回しで構いません。 動くものができたら、次の観点で 1〜2 周目を回すと現場感に近づきます。
- パラメータ化クエリになっているか(SQL インジェクション対策)
- 例外処理が抜けていないか(
SqlExceptionのキャッチ、MessageBox/ Razor での表示) - 確認ダイアログ・PRG パターン・
ModelStateが省略されていないか - 文字列補間、
varの使い分け、using宣言が雑になっていないか - 重複コードを Repository に寄せられないか
質問のしかた
Section titled “質問のしかた”本付録の課題で詰まったときは、講師に丸投げせず、次の 3 点をメモしてから 相談してください。
- 何をしようとしているか(課題と狙い)
- どこまで動いているか(画面ショット or 入力したコード)
- 何が起きているか(エラーメッセージ、期待した動きと実際の差)
これは配属後にも効く、現場の質問の型 です。
F-2 準備:共通の前提
Section titled “F-2 準備:共通の前提”本付録は、本編の到達点(第 25 章 Win CRUD / 第 30 章 Web CRUD)を 下地として再利用 します。 始める前に、次のいずれかが手元にあることを確認してください。
| 必要なもの | 用途 | 章 |
|---|---|---|
Chapter25 ソリューション(Win CRUD) | F-3 系の出発点 | 第 25 章 |
Chapter30 ソリューション(Web CRUD) | F-4 系の出発点 | 第 30 章 |
Chapter26 ソリューション(占いアプリ) | F-5 系の出発点 | 第 26 章 |
TrainingDB(SQLServer) | 全 DB 系課題 | 第 16 章 |
| 第 17 章で接続できた状態 | 全 DB 系課題 | 第 17 章 |
F-3〜F-5 はそれぞれ独立しているので、どこから手を付けても構いません。
F-3 Windows フォーム編 発展課題
Section titled “F-3 Windows フォーム編 発展課題”第 23〜25 章の社員管理アプリ(Chapter25)を出発点にした発展課題です。
F-3-1 部署一括の給与アップ(トランザクション付き)
Section titled “F-3-1 部署一括の給与アップ(トランザクション付き)”- フォームに 「選択部署を一律 1 万円アップ」 ボタンを追加
- 部署プルダウン(本編で使ったもの)で部署を選択
- 「(すべての部署)」が選択されているときは、確認ダイアログで 強く警告(「全社員に適用されます」)
- 実行は トランザクション で行い、想定外の例外が起きたらロールバック
- 完了後、影響行数を
MessageBoxで表示
EmployeeRepositoryにBulkSalaryUp(int departmentId)を追加(部署 = -1 のとき WHERE を外す)- トランザクションは
SqlConnection.BeginTransaction()→SqlCommand.Transaction = txを必ずセット tryでCommit()、catchでRollback()→ 例外はthrowで呼び元に伝える- 「自分が今書いている SQL を一度 SSMS で打ってみる」と頭が整理される
骨格コード(EmployeeRepository に追加)
Section titled “骨格コード(EmployeeRepository に追加)”public int BulkSalaryUp(int departmentId, decimal amount){ using SqlConnection conn = new SqlConnection(_connectionString); conn.Open(); using SqlTransaction tx = conn.BeginTransaction(); try { // TODO: departmentId == -1 のときは WHERE 句を外す // TODO: パラメータ化クエリで UPDATE // TODO: tx.Commit(); // return 影響行数; } catch { tx.Rollback(); throw; }}提出前チェック
Section titled “提出前チェック”-
BeginTransaction/Commit/Rollbackが組になっている -
cmd.Transaction = tx;を忘れていない - 「(すべての部署)」時の確認文言が他の確認より明らかに強い
- 影響行数を画面で確認できる
F-3-2 社員一覧の CSV エクスポート
Section titled “F-3-2 社員一覧の CSV エクスポート”- フォームに「CSV 出力」ボタンを追加
- 押すと
SaveFileDialogでファイル名を選ばせ、現在のDataGridViewの内容を CSV で書き出す - 文字コードは UTF-8 with BOM(Excel で文字化けさせないため)
- 列順は画面と一致、ヘッダー行を 1 行目に出す
- カンマ・改行・ダブルクォートを含む値は、ダブルクォートで囲んで内部のダブルクォートをエスケープ(
"→"")
- 第 22 章の
StreamWriter+ 自前 CSV 整形の延長 using var writer = new StreamWriter(path, false, new UTF8Encoding(true));で BOM 付き- 「カンマ / 改行 / ダブルクォートのいずれかを含む」値は引用するロジックを 1 メソッドに切り出す:
private static string Escape(string value){ if (value == null) return ""; bool needsQuote = value.Contains(',') || value.Contains('"') || value.Contains('\n'); string escaped = value.Replace("\"", "\"\""); return needsQuote ? $"\"{escaped}\"" : escaped;}- 出力対象は
dataGridViewEmployees.DataSourceをList<Employee>にキャストして取り出すと簡単
提出前チェック
Section titled “提出前チェック”- Excel で開いて文字化けしない
- 検索結果のみがエクスポートされる(全件ではない)
- 1 行目がヘッダー
- カンマを含む
last_nameを作って試して、列がずれない
F-3-3 部署マスタ管理画面
Section titled “F-3-3 部署マスタ管理画面”- メインフォームに「部署マスタ」ボタンを追加
- 押すと
DepartmentMasterFormがモーダルで開き、departmentsテーブルの CRUD ができる - 画面構成:
DataGridView+ 編集ダイアログ(社員編集と同じパターン) - 注意点:外部キーで参照されている部署を削除しようとすると
SqlException(Number=547)が出る →MessageBoxでユーザーに知らせる
- 第 25 章の
EmployeeRepositoryパターンをそのままDepartmentRepositoryに複製 departmentsはmanager_idという列があるので、編集画面ではmanager_idをどう入力させるか考える(プルダウン? 数値入力?)- 部署マスタを更新したら、メインフォームの部署プルダウンの再読み込みを忘れない(
Form_Activatedで再読み込み or 戻り値で判定)
提出前チェック
Section titled “提出前チェック”- 部署の追加・編集・削除がパラメータ化クエリで動く
- 参照されている部署を削除しようとしたとき、
SqlException.Number == 547をキャッチして分かりやすいメッセージを出す - メインフォームに戻ったとき、部署プルダウンが最新になる
F-4 Web MVC 編 発展課題
Section titled “F-4 Web MVC 編 発展課題”第 27〜30 章の Web 社員管理アプリ(Chapter30)を出発点にした発展課題です。
F-4-1 社員一覧のページング
Section titled “F-4-1 社員一覧のページング”- 1 ページ 10 件で
/Employees?page=1のように指定できる - 画面下部に「前へ / 1 / 2 / 3 / 次へ」のページャを出す
- 現在ページは強調、
pageが範囲外なら 1 ページ目に丸める - 検索条件と併用できる(
/Employees?keyword=山&page=2)
- SQL は SQLServer 2012+ の
OFFSET ... FETCH NEXT ... ROWS ONLYが定番
SELECT ...FROM employeesORDER BY employee_idOFFSET @skip ROWSFETCH NEXT @take ROWS ONLY- 総件数は別途
SELECT COUNT(*)で取得(ExecuteScalar) EmployeeSearchViewModelにPage、PageCount、TotalCountを追加- ページャの View は
for (int p = 1; p <= Model.PageCount; p++) { ... }の繰り返し
提出前チェック
Section titled “提出前チェック”-
?page=0や?page=999でも崩れない - 検索条件と併用できる(リンクに
keywordも含めている) -
ORDER BYを必ず付ける(OFFSET は ORDER BY が無いと SQLServer がエラーを返す)
F-4-2 列ヘッダクリックでソート
Section titled “F-4-2 列ヘッダクリックでソート”<th>をクリックすると、その列で昇順 → クリックするたびに降順 → 昇順 と切り替わる- URL 例:
/Employees?sort=salary&desc=true&page=1 - ソート列名は ホワイトリストで検証(任意の文字列を直接 SQL に挿入しない ← SQL インジェクション対策)
- ソート対象は
string sortColumnで受け取り、Repository 内で許可リストと突き合わせる
private static readonly HashSet<string> AllowedSortColumns = new(){ "employee_id", "last_name", "first_name", "salary", "hire_date"};- 一覧
<th>の<a href>で sort パラメータを切り替える - ページングと組み合わせる場合、ソート変更時は
page=1にリセットしたほうがユーザーに優しい
提出前チェック
Section titled “提出前チェック”- 許可リスト外の列名を URL に入れても、エラーや SQL インジェクションが起きない
- 同じ列を 2 回クリックすると昇順 ⇄ 降順が入れ替わる
- ページングと両立する(F-4-1 をやっている場合)
F-4-3 CSV ダウンロード(Web)
Section titled “F-4-3 CSV ダウンロード(Web)”/Employees/Csvで 現在の検索条件に一致する全件 を CSV で返す- Content-Type は
text/csv、ファイル名はemployees_yyyyMMdd_HHmmss.csv - 一覧画面のボタン「CSV ダウンロード」から飛ぶ
- Controller のアクションは
FileContentResultを返すと簡単
public IActionResult Csv(string keyword, int departmentId){ List<Employee> list = _employeeRepository.Search(keyword ?? "", departmentId); byte[] bytes = ...; // UTF-8 with BOM で組み立てた CSV string fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; return File(bytes, "text/csv", fileName);}StringBuilderで組み立ててEncoding.UTF8.GetBytes(...)でバイト列に- BOM を入れるには先頭に
0xEF, 0xBB, 0xBFを書き出す(new UTF8Encoding(true).GetPreamble()) - F-3-2 の Escape ロジックは Web 版でもそのまま流用できる
提出前チェック
Section titled “提出前チェック”- ダウンロードしたファイルを Excel で開いて文字化けしない
- 検索条件が反映されている(URL のクエリ文字列を Controller で受け取れている)
- ファイル名にタイムスタンプが入っている
F-5 占いアプリ 発展課題
Section titled “F-5 占いアプリ 発展課題”第 26 章の占いアプリ(Chapter26)を出発点にした発展課題です。
F-5-1 干支・星座の判定
Section titled “F-5-1 干支・星座の判定”- 結果画面に「あなたの干支」と「あなたの星座」を追加表示
- 干支:生年から計算(
(year - 4) % 12) - 星座:生月日から判定(12 種類、境界日に注意)
- ロジックは
Models/FortuneCalculator.csのような ロジック専用クラス に切り出す
- 干支は
string[] zodiac = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };でzodiac[(year - 4) % 12] - 星座は「月日 → 星座名」を返すメソッドを 1 つ書く。境界の月日(例:3 月 21 日 → 牡羊座)に注意
- Controller・View はロジックを持たない。
FortuneCalculator.GetChineseZodiac(year)のような呼び方に揃える
提出前チェック
Section titled “提出前チェック”- 生年月日に対して干支・星座が正しく出る
- 境界(例:4/19 と 4/20)で星座が切り替わる
- Controller が if 文の塊にならず、ロジッククラスに切り出されている
F-5-2 生きてきた日数
Section titled “F-5-2 生きてきた日数”- 結果画面に「あなたが生まれてから今日まで:○○日」を表示
- 計算は
(DateTime.Today - HireDate).Daysのような単純な引き算 - ついでに「○○時間 / ○○分」も表示できる(
TimeSpan.TotalHours/TotalMinutes)
TimeSpanのDays、TotalHours、TotalMinutes、TotalSecondsの違いを確認しておく- 未来の日付を入れたときに負の数を出さないよう、入力チェックも添える
提出前チェック
Section titled “提出前チェック”- 自分の生年月日で計算して、感覚と一致する
- 未来の日付を入れたときの挙動が「分かりやすい」(エラーメッセージ or 0 で固定)
F-5-3 決定論的ランダム(同じ人は同じ結果)
Section titled “F-5-3 決定論的ランダム(同じ人は同じ結果)”- 現在は 押すたびに結果が変わる(
new Random()の既定挙動) - これを「同じ人(同じ名前 + 生年月日)は、同じ日にやれば同じ結果」に変える
- 翌日になったら結果が変わる(「今日の運勢」っぽくなる)
Random(seed)のコンストラクタにシード値を渡すと、同じシードからは同じ乱数列が得られる- シード値は「名前のハッシュ + 生年月日の
Ticks+ 今日のTicks」を合算するなど
int seed = (name + birthDate.ToString("yyyyMMdd") + DateTime.Today.ToString("yyyyMMdd")).GetHashCode();Random random = new Random(seed);- ハッシュ値は実行ごとに変わる(同一プロセス内では安定)ことに注意。本格運用するなら
SHA256をバイト列にしてintに丸める のが安定する
提出前チェック
Section titled “提出前チェック”- 同じ入力を 5 回繰り返して、すべて同じ結果になる
- 違う名前を入れると、結果が違う
- 翌日になると結果が変わる(時刻を変えて確認するか、シードに今日を入れていることを目視確認)
F-6 横断テーマ(現場で出会うもの)
Section titled “F-6 横断テーマ(現場で出会うもの)”ここからは「Win か Web かに依らず、現場で出会う技術」をいくつか紹介します。 本編では研修負荷を抑えるために省略してきたものを、ここで体験できます。
F-6-1 appsettings.json + IConfiguration
Section titled “F-6-1 appsettings.json + IConfiguration”- 第 27〜30 章で
const string ConnectionString = "..."と直書きしていた接続文字列を、appsettings.jsonから読み込む形に変える - パスワードを コードからソース管理外に追い出す ことが目的
- Web プロジェクトには既に
appsettings.jsonがある。ConnectionStringsセクションを追加:
{ "ConnectionStrings": { "TrainingDB": "Server=localhost;Database=TrainingDB;User Id=app_user;Password=...;TrustServerCertificate=true;" }}Program.csでbuilder.Configuration.GetConnectionString("TrainingDB")で読める- 本来は DI コンテナで Repository に渡す(F-6-2 と合わせるとキレイ)が、まずは
Program.csで取ってEmployeeRepository.ConnectionStringの static フィールドに入れるだけでも体験になる - 本番運用では
appsettings.Development.jsonや ユーザーシークレット で開発者ごとに上書きする
提出前チェック
Section titled “提出前チェック”-
appsettings.jsonを変更すると、再起動だけで接続先が切り替わる -
.gitignoreで本物の接続文字列を含むファイルが Git に入らないようにする(本物のパスワードはコミットしない)
F-6-2 DI コンテナで Repository を渡す
Section titled “F-6-2 DI コンテナで Repository を渡す”- 第 27〜30 章では
new EmployeeRepository()を Controller の中で 直接 new していた - これを ASP.NET Core の DI コンテナ に登録し、Controller のコンストラクタで受け取る形に変える
Program.csで:
builder.Services.AddScoped<EmployeeRepository>();builder.Services.AddScoped<DepartmentRepository>();- Controller を:
public class EmployeesController : Controller{ private readonly EmployeeRepository _employeeRepository; private readonly DepartmentRepository _departmentRepository;
public EmployeesController(EmployeeRepository employeeRepository, DepartmentRepository departmentRepository) { _employeeRepository = employeeRepository; _departmentRepository = departmentRepository; } // ...}- F-6-1 と組み合わせて、Repository が
IConfigurationから接続文字列を受け取る形にすると、業務 Web アプリの典型構成になる - 第 27 章 27-11 で紹介した「現場のお作法」を、自分の手で実装することになる
提出前チェック
Section titled “提出前チェック”- Controller から
new EmployeeRepository()が消えている - 接続文字列が
appsettings.jsonから読まれている(F-6-1 とセット) - Repository をコンストラクタで差し替えられる(=単体テストを書くときに差し込める)構造になっている
テストフレームワーク(xUnit など)について
本来であれば、ここに「xUnit で Repository をテストする」課題を置きたいところです。ただし、xUnit テストプロジェクトを追加すると
xunit/xunit.runner.visualstudio/Microsoft.NET.Test.Sdkなどの NuGet パッケージが自動で取り込まれます。研修環境では 追加 NuGet パッケージは事前に管理者の検査・申請が必要 なため、本付録ではテスト導入の課題は外しています。配属後、自分のチームで使えるテストフレームワーク(xUnit、NUnit、MSTest)が決まったら、そこに合わせて学んでください。
なお、F-6-2 の DI 化を済ませておくと、テストで Repository を差し替えやすくなります。先んじてやっておいて損はない準備です。
F-7 提出のしかた(任意)
Section titled “F-7 提出のしかた(任意)”本付録の課題は 任意提出 です。提出するなら、自分のリポジトリに次のような構成で残してください。
Kadai_AppendixF/├── F-3-1_BulkSalaryUp/ ← Windows フォーム編 F-3-1├── F-3-2_CsvExport/ ← Windows フォーム編 F-3-2├── F-4-1_Paging/ ← Web 編 F-4-1├── F-5-1_Zodiac/ ← 占いアプリ編 F-5-1└── README.md ← どの課題に取り組んだか、感想・詰まった点README.md には、最低限:
- 取り組んだ課題の一覧
- 各課題で 詰まった点 と どう解決したか
- 「次に作るとしたら何を変えるか」のメモ
を書くと、配属後にも振り返る価値のあるドキュメントになります。
レビューを希望する場合は、特定の 1〜2 課題に絞って 講師・先輩に依頼してください。複数同時のレビュー依頼は、お互いに焦点がぼやけます。
F-8 ここから先へ
Section titled “F-8 ここから先へ”本付録は 本編から少し離れる練習 をするための場でした。 さらに先へ進みたい場合の指針は、第 31 章「11節 次に学ぶなら」 にまとめてあります。
- Windows アプリ深掘り(WPF、WinUI、MAUI、MVVM)
- Web 深掘り(Razor Pages、Blazor、認証認可)
- DB 深掘り(EF Core、Dapper、クエリチューニング)
- 現場の基礎(Git ブランチ運用、テスト、設計と読みやすさ)
本付録の課題で「もっと深くやりたい」と感じたテーマがあれば、それが次に学ぶべきテーマです。
たとえば:
| 本付録で楽しかった課題 | 次に学ぶと深まるテーマ |
|---|---|
| F-3-1 トランザクション | トランザクション分離レベル、デッドロック、楽観/悲観ロック |
| F-3-2 / F-4-3 CSV エクスポート | 大量データのストリーム処理、メモリ効率、SqlDataReader の早期解放 |
| F-3-3 部署マスタ管理 | マスタ管理画面の設計パターン、コードの再利用、UserControl |
| F-4-1 ページング / F-4-2 ソート | クエリチューニング、複合インデックス、実行計画 |
| F-5-1 干支・星座 | 計算ロジックの切り出し、テーブル駆動でのデータ管理 |
| F-6-1 / F-6-2 設定 + DI | Options パターン、IOptions<T>、ライフサイクル(Singleton/Scoped/Transient)、テスト戦略全般 |
何から手を付けるか迷ったときは、配属先のリポジトリで実際に使われている技術 を優先してください。本編で身に付けた Repository / SqlParameter / 入力チェック / PRG パターン は、どの道を選んでも土台になります。
本付録は、研修のゴールテープを切ったあとに、もう一段スピードを上げて走りたい人のために置いた 練習トラック です。 1 つも手を付けなくても研修は完成しています。1 つでも完成させたら、それは配属先で語れる成果になります。
無理せず、興味のあるところから始めてください。