0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ubuntu 26.04 + VS Code で ASP.NET Core MVC アプリを作る(SQL Server は Docker で)

0
Last updated at Posted at 2026-07-23

ASP.NET Core MVC の入門記事はほとんどが Windows 前提で、LocalDB を使う手順になっています。しかし LocalDB は Windows 専用で、Linux には存在しません。

この記事では Ubuntu 26.04 上で、書籍一覧アプリ(CRUD)を最後まで動かします。

  • DB は SQL Server を Docker で立てて代替する
  • 接続文字列は user-secrets に逃がす
  • 初期データは EF Core の HasData でマイグレーションに焼き込む

Windows 記事をなぞると必ず詰まる箇所が5つあるので、そこも実際のエラーメッセージ付きで解説します。

前提

$ cat /etc/os-release | head -2
PRETTY_NAME="Ubuntu 26.04 LTS"

$ dotnet --version
10.0.302

$ docker version --format '{{.Server.Version}}'
29.5.2

最終的なディレクトリ構成はこうなります。

BookList/
├── compose.yaml          # SQL Server
├── .env                  # SAパスワード(gitignore)
├── .env.example
├── .vscode/
│   ├── launch.json
│   └── tasks.json
├── BookList.slnx
└── BookList/
    ├── Models/
    │   ├── Book.cs
    │   └── BookContext.cs
    ├── Controllers/BooksController.cs
    ├── Views/Books/
    └── Migrations/

ステップ1: SQL Server を Docker で用意する

LocalDB の代わりに SQL Server コンテナを使います。compose.yaml を作ります。

name: booklist

services:
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    container_name: mssql-booklist
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_PID: Developer
      MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:?.env を用意してください}
    ports:
      - "1433:1433"
    volumes:
      - mssql-data:/var/opt/mssql
    restart: unless-stopped
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$MSSQL_SA_PASSWORD\" -C -Q 'SELECT 1' || exit 1",
        ]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 30s

volumes:
  mssql-data:

パスワードは .env に置きます。

# .env
MSSQL_SA_PASSWORD=DevP@ssw0rd2026
echo ".env" >> .gitignore
docker compose up -d

ここが重要なポイント

  1. MSSQL_PID: Developer を指定する
    エディションは環境変数で切り替えます。Developer 版は開発用途なら無料で、Express 版のような「DB 10GB・メモリ1.4GB・4コア」の制限がありません。本番想定なら Express にします。

  2. volumes は必須
    これを書かないと DB の実体がコンテナの書き込みレイヤーに置かれ、docker rm した瞬間に消えます。名前付きボリュームにしておけば docker compose down してもデータは残ります(消したいときだけ down -v)。

  3. healthcheck$$ はエスケープ
    $$MSSQL_SA_PASSWORD と2つ重ねることで compose 側の変数展開を抑止し、コンテナ内のシェルに展開させます。$ 1つだと空文字になります。

起動確認。

$ docker compose ps
SERVICE   NAME             STATUS
db        mssql-booklist   Up 16 seconds (healthy)

(healthy) が出れば SQL Server が接続を受け付けています。

ステップ2: プロジェクトを作る

dotnet new sln -o BookList
cd BookList
dotnet new mvc -o BookList -f net10.0
dotnet sln BookList.slnx add BookList/BookList.csproj

.NET 10 では .sln ではなく .slnx が生成されます。
新しい XML ベースのソリューション形式が既定になりました。Windows 記事のとおり dotnet sln BookList.sln add ... と打つと
Could not find solution or directory 'BookList.sln' で失敗します。

ステップ3: ツールとパッケージを入れる

dotnet tool install --global dotnet-ef
dotnet tool install --global dotnet-aspnet-codegenerator
cd BookList
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

パッケージは Windows 記事とまったく同じです。SQL Server を使う構成なので Microsoft.EntityFrameworkCore.SqlServer がそのまま使えます。ここが SQLite や PostgreSQL に置き換える場合との大きな違いで、コード変更が一切要りません。

ステップ4: モデルと DbContext

Models/Book.cs

namespace BookList.Models;

public class Book
{
    public int Id { get; set; }
    public string Isbn { get; set; } = string.Empty;
    public string Title { get; set; } = string.Empty;
    public int Price { get; set; }
    public string Publisher { get; set; } = string.Empty;
    public DateTime Published { get; set; }
    public bool Sample { get; set; }
}

Models/BookContext.cs — 初期データも HasData でここに書きます。

using Microsoft.EntityFrameworkCore;

namespace BookList.Models {
    public class BookContext : DbContext {
        public BookContext (DbContextOptions options) : base (options) { }
        public DbSet<Book> Books { get; set; }

        protected override void OnModelCreating (ModelBuilder modelBuilder) {
            // マイグレーションに INSERT として焼き込まれる
            modelBuilder.Entity<Book> ().HasData (
                new Book { Id = 1, Isbn = "978-4-7981-0000-1", Title = "ASP.NET Core MVC 入門",
                           Price = 3800, Publisher = "翔泳社",
                           Published = new DateTime (2026, 4, 15), Sample = true },
                new Book { Id = 2, Isbn = "978-4-7981-0000-2", Title = "独習 C# 第6版",
                           Price = 4200, Publisher = "翔泳社",
                           Published = new DateTime (2025, 11, 20), Sample = false },
                new Book { Id = 3, Isbn = "978-4-2971-0000-3", Title = "Entity Framework Core 実践ガイド",
                           Price = 3600, Publisher = "技術評論社",
                           Published = new DateTime (2026, 1, 30), Sample = true },
                new Book { Id = 4, Isbn = "978-4-8222-0000-4", Title = "C# パフォーマンスチューニング",
                           Price = 4800, Publisher = "日経BP",
                           Published = new DateTime (2025, 8, 5), Sample = false },
                new Book { Id = 5, Isbn = "978-4-8144-0000-5", Title = ".NET マイクロサービス設計",
                           Price = 5200, Publisher = "オライリー・ジャパン",
                           Published = new DateTime (2026, 6, 10), Sample = true }
            );
        }
    }
}

HasData を使うと、DB を作り直しても dotnet ef database update だけで同じ初期状態が復元されます。SQL を手打ちする方式と違い、データが手順書の外に散らばりません。主キーは明示が必須なので Id を必ず書きます。

ステップ5: 接続文字列を user-secrets に置く

Windows 記事では appsettings.json に接続文字列を書きます。

"ConnectionStrings": {
  "BookContext": "Server=(localdb)\\mssqllocaldb;Database=BookList;Trusted_Connection=True;"
}

Linux では (localdb)Trusted_Connection(Windows統合認証)も使えません。SA アカウントでのログインになりますが、パスワードを appsettings.json に書くとリポジトリに入ってしまいます。user-secrets に逃がします。

dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:BookContext" \
  "Server=localhost,1433;Database=BookList;User Id=sa;Password=DevP@ssw0rd2026;TrustServerCertificate=True;MultipleActiveResultSets=True;"

保存先はリポジトリの外(~/.microsoft/usersecrets/<UserSecretsId>/secrets.json、パーミッション600)です。

Program.cs

using Microsoft.EntityFrameworkCore;
using BookList.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

// 未設定なら起動時に落とす(null が DB ドライバまで流れると原因が分かりにくい)
var connectionString = builder.Configuration.GetConnectionString("BookContext")
    ?? throw new InvalidOperationException(
        "接続文字列 'BookContext' が見つかりません。user-secrets を設定してください。");

builder.Services.AddDbContext<BookContext>(
    options => options.UseSqlServer(connectionString));

var app = builder.Build();

TrustServerCertificate=True は必須です。コンテナの SQL Server は自己署名証明書を使うため、これがないと接続時に証明書エラーで落ちます。

ステップ6: マイグレーションと DB 作成

dotnet ef migrations add Initial
dotnet ef database update

dotnet ef は user-secrets を追加フラグなしで読んでくれます。確認するならこれ。

$ dotnet ef dbcontext info
Provider name: Microsoft.EntityFrameworkCore.SqlServer
Database name: BookList
Data source: localhost,1433

投入結果を SQL で直接見てみます。

docker compose exec db /opt/mssql-tools18/bin/sqlcmd \
  -S localhost -U sa -P "$(grep MSSQL_SA_PASSWORD ../.env | cut -d= -f2)" \
  -C -d BookList -Q "SELECT Id, Title, Publisher FROM Books;" -W -s "|"
Id|Title                            |Publisher
1 |ASP.NET Core MVC 入門            |翔泳社
2 |独習 C# 第6版                    |翔泳社
3 |Entity Framework Core 実践ガイド |技術評論社
4 |C# パフォーマンスチューニング    |日経BP
5 |.NET マイクロサービス設計        |オライリー・ジャパン

ステップ7: コントローラーとビューを自動生成

dotnet aspnet-codegenerator controller -name BooksController \
  -outDir Controllers -async -m Book -dc BookContext -udl -scripts
Added Controller : '/Controllers/BooksController.cs'.
Added View : /Views/Books/Create.cshtml
Added View : /Views/Books/Edit.cshtml
Added View : /Views/Books/Details.cshtml
Added View : /Views/Books/Delete.cshtml
Added View : /Views/Books/Index.cshtml

このコマンドは Windows 記事と完全に同一で通ります。 Linux だからといって書き換える必要はありません。

ステップ8: 起動する

dotnet watch --launch-profile https
Now listening on: https://localhost:7127
Now listening on: http://localhost:5275
Application started.

https://localhost:7127/Books にアクセスすれば CRUD が動きます。

CLI でも確認できます。

$ curl -sk -o /dev/null -w "%{http_code}\n" https://localhost:7127/Books
200

# 一覧の行数(編集リンクの数)
$ curl -sk https://localhost:7127/Books | grep -c '/Books/Edit/'
5

Ubuntu ならではのハマりどころ

ここからが本題です。Windows 記事どおりに進めると必ず引っかかる箇所をまとめます。

1. dotnet dev-certs https --trust が効ききらない

[110] For OpenSSL trust to take effect, '$HOME/.aspnet/dev-certs/trust' must be
      listed in the SSL_CERT_DIR environment variable.
There was an error trusting the HTTPS developer certificate.
It will be trusted by some clients but not by others.

証明書自体は作られますが、ブラウザの信頼ストアまでは登録されません。警告を消すには2つ必要です。

sudo apt install libnss3-tools
echo 'export SSL_CERT_DIR="$HOME/.aspnet/dev-certs/trust:/usr/lib/ssl/certs"' >> ~/.bashrc

curl で試すときは -k を付ければ回避できます。

2. 日本語が HTML 数値文字参照に化ける

ページのソースを見るとこうなっています。

<td>ASP.NET Core MVC &#x5165;&#x9580;</td>

これは ASP.NET Core の既定 HtmlEncoder非ASCII文字をすべてエスケープする仕様によるものです。ブラウザ表示は正常なので実害はありませんが、ソースが読みづらく、転送量も増えます。

Program.cs に追加すれば解消します。

using System.Text.Encodings.Web;
using System.Text.Unicode;
using Microsoft.Extensions.WebEncoders;

builder.Services.Configure<WebEncoderOptions>(
    options => options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All));

TextEncoderSettingsSystem.Text.Encodings.WebUnicodeRangesSystem.Text.Unicode と、名前空間が別です。片方だけだと CS0246 で落ちます。

3. Hot Reload が効かない変更がある

user-secrets initOnModelCreating の追加をすると、こう出ます。

dotnet watch ❌ error ENC0021: Adding attribute requires restarting the application.
dotnet watch ❌ error ENC0023: Adding an abstract method or overriding an inherited method
                              requires restarting the application.
dotnet watch 🔥 Restart is needed to apply the changes.

厄介なのは、このとき古いプロセスがそのまま生き続けることです。appsettings.json から接続文字列を消した直後にこれが起きると、設定のホットリロードだけが効いて接続文字列が null になり、原因不明の 500 エラーになります。

素直に Ctrl+C → 再実行してください。

4. pkill -f 'dotnet watch' は自分ごと殺す

プロセスが残ったとき、これをやると自分のシェルまで巻き込みます(コマンドライン文字列自体がパターンにマッチするため)。

# NG
pkill -f 'dotnet watch'

# OK: PID を特定してから
ps -eo pid,args | grep '[d]otnet-watch\.dll'
kill -9 <PID>

grep のパターンを [d]otnet と書くのは、grep 自身がヒットしないようにする定番のテクニックです。

5. HasData の主キーは既存行と衝突する

すでに手動でデータを入れている状態で HasData を足すと、マイグレーションの InsertData が主キー違反で失敗します。先にテーブルを空にしてから適用してください。

docker compose exec db /opt/mssql-tools18/bin/sqlcmd \
  -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -d BookList -Q "DELETE FROM Books;"
dotnet ef database update

まとめ

Ubuntu で ASP.NET Core MVC を動かす要点は3つです。

  • LocalDB は Docker の SQL Server で置き換えるMSSQL_PID: Developer なら開発用途は無料で制限なし。パッケージもコマンドも Windows 記事のまま通る
  • 接続文字列は user-secrets へ(localdb)Trusted_Connection が使えないため SA 認証になるが、パスワードをリポジトリに入れない
  • volumeshealthcheck を必ず書く。前者がないとコンテナ削除でデータ消失、後者がないと起動途中の DB に繋ぎにいって失敗する

日本語のエンコードと Hot Reload の挙動は Windows でも起きますが、Linux では検証を CLI(curl + sqlcmd)で回すことが多く、数値文字参照の化けに気づきやすいと思います。

同じ構成のリポジトリ一式は compose.yamlREADME.md を置いておけば、docker compose up -ddotnet ef database updatedotnet watch の3コマンドで再現できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?