EF Core スキャフォールディングで出る「MSB1008: Only one project can be specified.」の原因と解決策【.NET 9/dotnet ef】

EF Core のスキャフォールディング中に「MSBUILD : error MSB1008: Only one project can be specified.」で止まる――.NET 9 の学習や複数プロジェクトのソリューションで起きやすい現象です。本記事では最短の解決手順から、エラーの正体、設計時ビルドの挙動、再発防止のフォルダー構成、運用時のチェックリストまでを一気に整理します。

目次

症状と前提(再現シナリオ)

次のようなコマンドで DbContext とエンティティを自動生成しようとしたところ、MSB1008 が出て失敗するケースです。

dotnet ef dbcontext scaffold "Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustCertificate=true;" \
  Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations

ソリューションには DataContextEntityModels の2プロジェクトがあり、いずれも本格実装は未着手の状態――まさに学習・初期構築でよく見る状況です。

エラーの正体:MSB1008 は「複数プロジェクトが指定された」

MSB1008 は MSBuild のメッセージで、平たく言えば「ビルド対象のプロジェクトが1つに定まっていない」ことを示すエラーです。dotnet ef は内部で MSBuild を呼び出し、「カレントディレクトリから見た既定のプロジェクト」を基準に設計時ビルド(Design-time build)を実行します。ところが次のような条件が重なると、対象プロジェクトを一意に決められなくなり MSB1008 が発生します。

  • ソリューション直下でコマンドを実行しており、同階層またはサブ階層に複数の .csproj が存在する
  • 複数プロジェクトが同じフォルダーに混在している(極めて起きやすい)
  • Visual Studio の「既定のプロジェクト」と CLI 実行ディレクトリの不一致

言い換えると、「どの .csproj をビルドすれば良いのか」を EF Core が決めきれなかった結果です。

最短解決:まずはこの3つを押さえる

やること詳細備考
① コマンド実行場所を正すdotnet efカレントディレクトリのプロジェクトを対象にします。ソリューション直下ではなく、スキャフォールディングを生成したいプロジェクト(ここでは DataContext)のフォルダーに移動してから実行します。cd .\DataContext のように移動してからコマンド再実行。
② プロジェクトを別フォルダーに分離*.csproj が同一フォルダーに並ぶ構成は MSBuild の解決を難しくします。
例:/DataContext/DataContext.csproj/EntityModels/EntityModels.csproj のように分ける。
分けた後は dotnet sln add <相対パス> でソリューションに登録。
③ 明示的にプロジェクトを指定場所を移動できない場合は --project と、必要に応じて --startup-project を付けて対象を明示します。例:
--project .\DataContext\DataContext.csproj --startup-project .\DataContext\DataContext.csproj

正しい実行例(OS別)

Windows PowerShell(一行で実行)

cd .\DataContext
dotnet ef dbcontext scaffold "Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations

Windows PowerShell(移動せずに指定)

dotnet ef dbcontext scaffold "Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations --project .\DataContext\DataContext.csproj --startup-project .\DataContext\DataContext.csproj

macOS / Linux(Bash)

cd ./DataContext
dotnet ef dbcontext scaffold "Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations

補足:SQL Server の接続文字列では通常 TrustServerCertificate=true を使います(環境方針に合わせて変更してください)。

なぜ「DataContext」で実行するのか

スキャフォールディングで生成される DbContext とエンティティは、生成先プロジェクトに対してパッケージ参照・ビルドコンテキストが解決されます。DataContext を「実行プロジェクト(=ビルド対象)」にすると、必要なデザイン時依存関係(Microsoft.EntityFrameworkCore.Design など)が正しく検出され、設計時ビルドが通ります。逆にソリューション直下のような曖昧な場所で実行すると、複数の .csproj が見つかり MSB1008 につながります。

–project と –startup-project の違い

オプション役割指定の目安
--projectEF コマンドの対象プロジェクト(メタデータや出力先の基準)。エンティティや DbContext のソースがここに出力されます。モデル層や DbContext を含むライブラリを指定。--project .\DataContext\DataContext.csproj
--startup-project設計時にアプリを起動するエントリプロジェクト。接続文字列や DI 構成をここから解決する場合に指定。Web/API などの実行エントリが別にある場合に指定。--startup-project .\Api\Api.csproj

今回のように DataContext 単独で完結させるなら、両方とも DataContext を指しておくのが手堅いです。

ディレクトリ設計のベストプラクティス

MySolution/
├─ MySolution.sln
├─ DataContext/
│  ├─ DataContext.csproj
│  └─ (生成される) NorthwindContext.cs / モデル類
├─ EntityModels/
│  └─ EntityModels.csproj
└─ Api/
   └─ Api.csproj
  • 各プロジェクトは必ず専用フォルダーに分ける
  • ソリューションに追加:dotnet sln add .\DataContext\DataContext.csproj など
  • 参照を明示:DataContext -> EntityModels が必要なら dotnet add .\DataContext\DataContext.csproj reference .\EntityModels\EntityModels.csproj

再スキャフォールディング時の安全なオプション設計

目的主なオプションポイント
出力先を整理--output-dir(モデル)、--context-dir(DbContext)--output-dir Models --context-dir Contexts生成物を分離して差分を把握しやすく。
名前空間分離--namespace--context-namespace--model-namespace--context-namespace Northwind.Data --model-namespace Northwind.EntityModels設計ポリシーに合わせて厳密に。
上書き制御--force--force既存変更が消えないよう、まずは出力先を分けるのが安全。
命名の保存--use-database-names--no-pluralize--use-database-names --no-pluralizeDB そのままの命名を保つ/複数形規則を無効化。
生成対象の限定--schema--table--schema dbo --table dbo.Products --table dbo.Orders巨大 DB での初回取得時間を短縮。

接続文字列の取り扱い(安全と可搬性)

  • 開発時はユーザーシークレットに退避:dotnet user-secrets initdotnet user-secrets set "ConnectionStrings:Northwind" "<接続文字列>"
  • アプリ側で IConfiguration から参照すれば --startup-project による解決も可能
  • TrustServerCertificate=true の利用有無はセキュリティ方針に合わせて判断
  • PowerShell と Bash でのクォート差異(;, を含む場合)は特に注意

ユーザーシークレットから解決する例:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Northwind" Microsoft.EntityFrameworkCore.SqlServer --project .\DataContext\DataContext.csproj --startup-project .\Api\Api.csproj

よくある落とし穴と対処

症状原因対処
MSB1008 が消えない同階層に複数の .csproj/ソリューション直下で実行対象プロジェクトに cd するか --project を付与
dotnet ef 自体が見つからないEF ツール未インストール/古いバージョンdotnet tool update --global dotnet-ef
ヘルプに dbcontext scaffold が出ないツールと SDK の不整合/プロジェクト未復元dotnet --info で SDK を確認、dotnet restore
依存パッケージ不足エラーMicrosoft.EntityFrameworkCore.Design などが抜けているdotnet add package Microsoft.EntityFrameworkCore.Design
設計時 DbContext の解決に失敗DI 構成/環境変数前提のコードIDesignTimeDbContextFactory<T> 実装で設計時の生成を明示

設計時ビルドの理解を1分で

dotnet ef はコマンド実行時に対象プロジェクトを 設計時(Design-time) でビルドし、DbContext をインスタンス化してメタデータ(モデル)を取得します。ここで「どの csproj をビルドするか」が定まらなければ MSB1008 となるわけです。複数プロジェクトが絡むソリューションでは、--project--startup-project を使って明示的に解決する癖をつけると安定します。

最小構成での動作確認レシピ

  1. プロジェクトを作成(例:クラスライブラリ)
    dotnet new classlib -n DataContext dotnet sln add .\DataContext\DataContext.csproj
  2. 必須パッケージを追加
    dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.SqlServer dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Design
  3. DataContext に移動してスキャフォールディング
    cd .\DataContext dotnet ef dbcontext scaffold "Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations --output-dir Models --context-dir Contexts

ここまでで通れば、MSB1008 の根はほぼ解消されています。以降は構成分割や命名の最適化に進めます。

IDesignTimeDbContextFactory でより堅牢に

アプリの起動設定や環境変数に依存する DbContext だと、設計時の生成が失敗しがちです。以下のようなファクトリを DataContext に置いておくと、--startup-project を工夫せずとも安定します。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace Northwind.Data;

public sealed class NorthwindContextFactory : IDesignTimeDbContextFactory
{
public NorthwindContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder()
.UseSqlServer("Data Source=tcp:127.0.0.1,1433;Initial Catalog=Northwind;User Id=sa;Password=s3cret-Ninja;TrustServerCertificate=true;")
.Options;
return new NorthwindContext(options);
}
} 

本番ではユーザーシークレットや環境変数に切り替えるなど、機密情報の取り扱いだけは忘れずに。

コマンドレシピ集(コピペ用)

プロジェクトを明示して実行

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Northwind" Microsoft.EntityFrameworkCore.SqlServer ^
  --project .\DataContext\DataContext.csproj ^
  --startup-project .\DataContext\DataContext.csproj ^
  --output-dir Models ^
  --context-dir Contexts ^
  --context NorthwindContext ^
  --data-annotations ^
  --use-database-names

ソリューション整備

dotnet sln add .\DataContext\DataContext.csproj
dotnet sln add .\EntityModels\EntityModels.csproj
dotnet add .\DataContext\DataContext.csproj reference .\EntityModels\EntityModels.csproj

ツールの健全性チェック

dotnet --info
dotnet --list-sdks
dotnet tool update --global dotnet-ef
dotnet ef --version

MSB1008 を二度と出さないためのチェックリスト

  • カレントディレクトリを明確に:実行前に pwd/Get-Location で必ず確認
  • プロジェクト分離:各プロジェクトは専用フォルダーを持たせる
  • 明示的オプション--project--startup-project を常用
  • ツール更新dotnet-ef を定期更新、SDK と整合
  • 生成先分離--output-dir--context-dir で上書き事故を防ぐ

トラブルが続くときの深掘りポイント

  1. ビルドを単体で試すdotnet build .\DataContext\DataContext.csproj -nologo -v:m(ビルド自体が通るか)
  2. 詳細ログdotnet ef dbcontext list --verbose --project .\DataContext\DataContext.csproj
  3. SDK の固定:チームでズレる場合は global.json を配置して SDK を固定
  4. Visual Studio 既定プロジェクト:CLI と食い違う挙動の温床。CLI はディレクトリ依存だと再認識

まとめ

MSB1008 は「ビルド対象が1つに定まっていない」ことのシグナルです。① 対象プロジェクトで実行する② プロジェクトをフォルダーで分離する③ 必要に応じて --project--startup-project を明示する――この3点を守れば、EF Core のスキャフォールディングは .NET 9 環境でも安定して動作します。さらに出力先・名前空間・生成対象を適切に制御すれば、再生成や規模拡大にも耐える「壊れにくい」プロジェクトが出来上がります。

補足:最初に入れておくと良い NuGet パッケージ

パッケージ用途導入コマンド(例)
Microsoft.EntityFrameworkCore.SqlServerSQL Server プロバイダーdotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Design設計時コンポーネント(必須)dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.ToolsVS 連携(PMC コマンド等)。CLI 中心なら必須ではないdotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Tools

補足:スキャフォールディングの品質を高める設定例

&lt;Project Sdk="Microsoft.NET.Sdk"&gt;
  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net9.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
  &lt;/PropertyGroup&gt;
  &lt;ItemGroup&gt;
    &lt;PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="&lt;latest&gt;" /&gt;
    &lt;PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="&lt;latest&gt;" PrivateAssets="all" /&gt;
  &lt;/ItemGroup&gt;
&lt;/Project&gt;

PrivateAssets="all" は上位プロジェクトへの設計時依存のリークを防ぎます。設計時専用の参照は上位に公開しないのが鉄則です。

ケーススタディ:モデルとコンテキストを別プロジェクトに分けたい

EntityModels を POCO のみ(属性だけ)にし、DataContext にコンテキストと Fluent API を置く構成も実務ではよく使われます。ポイントは「どちらを スキャフォールディングの対象にするか」です。属性でコントロールしたいなら EntityModels、Fluent API でコントロールしたいなら DataContext を対象にします。いずれにせよ、--project/--startup-project一意化して実行すれば MSB1008 は回避できます。

運用の小技:失敗しても被害を最小化する

  • 常に --output-dir--context-dir を付けて生成物を「箱」に閉じ込める
  • 生成結果の差分は VCS(Git)で確認してからマージ(--force の前に --output-dir で検証)
  • 巨大 DB の初回はテーブルを限定(--table を複数指定)し、のちに範囲を広げていく

最後に

「どのプロジェクトで実行するか」を明確にするだけで、MSB1008 は確実に避けられます。学習中でもチーム開発でも、ディレクトリの分離プロジェクトの明示を最初に決める――この小さな約束が、.NET 9 時代の EF Core 開発を驚くほどスムーズにしてくれます。

コメント

コメントする

目次