GitHub:https://github.com/ynakao55/aspnet-booklog
「本のタイトル・著者・メモ・ステータス(未読/読書中/読了)」を管理する読書ログ Web アプリを、ASP.NET Core + EF Core + PostgreSQLで作り、Dockerで動かしました。
この記事はポートフォリオ向けに、プロジェクト構成・主要ファイルの役割と作成ポイント・実行/デプロイ手順・ハマりどころをまとめたものです。
目次
1. アプリの説明
1-1. 何をするアプリ?
- 書籍(Title/Author/Note)を登録・一覧表示
- ステータス(
unread/reading/done)でフィルタ - 一覧は新しい順に表示、Note は長文でもトリムして見やすく(例:先頭 30 文字 + 省略記号)
1-2. 主な機能
- CRUD(今回は Create + Read の最小構成)
- フィルタ(クエリ文字列 or 画面リンク)
- 画面レイアウト(ヘッダー/フッターをレイアウトファイルで共通化)
- 初回起動時に自動マイグレーションでテーブル作成
1-3. 技術スタック
- フレームワーク:ASP.NET Core 8(Razor Pages)
- ORM:Entity Framework Core + Npgsql Provider
- DB:PostgreSQL
- インフラ:Docker
- 言語:C#
- OS/実行:Linux コンテナ
2. 学んだ技術とハマりどころ
- EF Core のマイグレーション
Docker 内でdotnet-efをローカルツールとして実行し、src/Migrations/をGit にコミットしておくのが本番デプロイを安定させるコツ。 - 自動マイグレーション
Program.cs起動時にdb.Database.Migrate()を呼ぶことで、無料プラン(Pre-Deploy コマンドなし)でも初回にテーブルが作成されるようにした。 - Docker マルチステージ
SDK イメージでビルド → ランタイムイメージに成果物だけコピー。軽量で速い。 - Razor Pages の構造化
_Layout.cshtmlにヘッダー/フッター/メニューをまとめ、Indexは一覧とフィルタ UI に集中。
3. プロジェクト構成と主要ファイル(抜粋)
aspnet-booklog/
├─ src/
│ ├─ aspnet-booklog.csproj
│ ├─ Program.cs # 起動、DI、DB接続、Auto Migrate
│ ├─ Data/
│ │ └─ AppDbContext.cs # EF Core DbContext(Books)
│ ├─ Models/
│ │ └─ Book.cs # エンティティ(Id/Title/Author/Status/Note)
│ ├─ Pages/
│ │ ├─ Index.cshtml.cs # 一覧/フィルタのハンドラ
│ │ ├─ Index.cshtml # 一覧画面(テーブル + フィルタ UI)
│ │ └─ Shared/
│ │ └─ _Layout.cshtml # 共通レイアウト
│ └─ Migrations/ # 生成されたマイグレーション(Git 管理)
├─ Dockerfile
└─ .dockerignore
3-1) Program.cs
役割:サービス登録、DATABASE_URL の再構成、DbContext の DI、起動時に Migrate()。
作成ポイント
Environment.GetEnvironmentVariable("DATABASE_URL")を読み、postgres://→postgresql://に置換。NpgsqlConnectionStringBuilderでSslMode=Require、TrustServerCertificate=truebuilder.Services.AddDbContext<AppDbContext>(opt => opt.UseNpgsql(connString));- 起動直後に
using var scope = app.Services.CreateScope(); db.Database.Migrate();。
3-2) Data/AppDbContext.cs
役割:EF Core のコンテキスト。
作成ポイント
- EF Core を使って
Bookエンティティをデータベースにマッピング - アプリケーションからデータベース操作を行うための設定を行う。
3-3) Models/Book.cs
役割:エンティティ。
作成ポイント
- バリデーション属性(
[Required]、[MaxLength])を付ける。
[Required, MaxLength(120)]
public string Title { get; set; } = "";
3-4) Pages/Index.cshtml.cs
役割:一覧表示&フィルタのハンドラ(OnGet)。
作成ポイント
- クエリ
statusを受け取って LINQ で絞り込み。 - 新しい順に並べる:
OrderByDescending(b => b.Id)。 - ページモデルに
BooksとCurrentStatusを用意。
擬似コード例:
public class IndexModel : PageModel
{
private readonly AppDbContext _db;
public IReadOnlyList<Book> Books { get; private set; } = [];
public BookStatus? CurrentStatus { get; private set; }
public IndexModel(AppDbContext db) => _db = db;
public async Task OnGetAsync(string? status)
{
IQueryable<Book> q = _db.Books.AsNoTracking();
if (Enum.TryParse<BookStatus>(status, true, out var st))
{
CurrentStatus = st;
q = q.Where(b => b.Status == st);
}
Books = await q.OrderByDescending(b => b.Id).ToListAsync();
}
}
3-5) Pages/Index.cshtml
役割:一覧テーブル&フィルタ UI。
作成ポイント
- 先頭に「[New Book]」ボタン、右側にフィルタ(All / Unread / Reading / Done)。
- Note はトリムして表示。
- 0 件時もフィルタは表示(UX が安定)。
UI のポイント(抜粋):
<div class="toolbar">
<a class="btn" href="/Create">New Book</a>
<span class="filters">
Filter:
<a href="/">All</a> |
<a href="/?status=Unread">Unread</a> |
<a href="/?status=Reading">Reading</a> |
<a href="/?status=Done">Done</a>
</span>
</div>
<table class="table">
<thead>
<tr>
<th>Title</th><th>Author</th><th>Status</th><th>Note</th><th></th>
</tr>
</thead>
<tbody>
@foreach (var b in Model.Books)
{
<tr>
<td>@b.Title</td>
<td>@b.Author</td>
<td>@b.Status</td>
<td>@(string.IsNullOrEmpty(b.Note) ? "" : (b.Note.Length <= 30 ? b.Note : b.Note.Substring(0,30) + "…"))</td>
<td><a href="/Details?id=@b.Id">Show</a></td>
</tr>
}
</tbody>
</table>
3-6) Pages/Shared/_Layout.cshtml
役割:サイト共通の枠(ヘッダー/フッター/メニュー/スタイル)。
作成ポイント
- ヘッダーにサイトタイトル「WEBアプリ開発教室」等を置き、メニューに
HomeやNew Bookを設置。
3-7) Dockerfile
役割:マルチステージでビルドして軽量ランタイムにデプロイ。
作成ポイント
ASPNETCORE_URLS=http://0.0.0.0:8080を環境変数で指定。UseAppHost=falseでアルパイン系でも動作安定。
3-8) .dockerignore
役割:不要ファイルをビルドコンテキストから除外し、転送・ビルドを高速化。
**/bin/
**/obj/
**/.vs/
**/*.user
**/*.suo
.git
.gitignore
Dockerfile
4.aspnet-booklog の実行方法(ローカル / Web)
ここからは、URL短縮アプリ aspnet-booklog の実行方法をまとめます。
ローカル環境(WSL + Docker)で動かす方法と、公開リンクから試す方法の両方を記載しています。
ローカルで実行する方法(Docker / WSL)
前提
- WSL(Windows Subsystem for Linux)がインストール済み
- Docker Desktop がインストール済み
- WSL と Docker の連携設定(WSL integration)が有効
1. Git をインストール(未インストールの場合)
sudo apt update
sudo apt install -y git
2. リポジトリをクローン
git clone https://github.com/ynakao55/aspnet-booklog.git
cd aspnet-booklog
3. Docker Compose で起動
通常の起動 / 停止
docker compose up -d --build
docker compose down
確実に作り直して起動したい場合(推奨)
コンテナ・ボリューム・不要な関連コンテナも整理してから再作成します。
docker compose down -v --remove-orphans
docker compose up -d --build --force-recreate
4. ブラウザで開く
起動後、ブラウザで以下のURLを開きます。
http://localhost:8080
公開リンクから実行する方法(Web)
以下の公開URLからアクセスできます。
ローカル実行時補足(トラブルシュート)
コンテナの状態を確認
docker compose ps
ログを確認
docker compose logs -f
画面が開かない / 反映されない場合
docker compose down -v --remove-orphans
docker compose up -d --build --force-recreate
5. スクリーンショット

6. まとめ
- ASP.NET Core + EF Core + PostgreSQL を Docker で包むと、ローカルも本番も同じアーティファクトで再現性高く運用できます。
- Razor Pages はレイアウトとページモデルを分けると、UI とロジックの見通しが良く、ポートフォリオとしても読みやすい構成になりました。
付録:依存パッケージ(NuGet)
Npgsql.EntityFrameworkCore.PostgreSQLNpgsqlMicrosoft.EntityFrameworkCore.Design


コメント