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
ソリューションには DataContext と EntityModels の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 の違い
| オプション | 役割 | 指定の目安 | 例 |
|---|---|---|---|
--project | EF コマンドの対象プロジェクト(メタデータや出力先の基準)。エンティティや 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-pluralize | DB そのままの命名を保つ/複数形規則を無効化。 |
| 生成対象の限定 | --schema、--table | --schema dbo --table dbo.Products --table dbo.Orders | 巨大 DB での初回取得時間を短縮。 |
接続文字列の取り扱い(安全と可搬性)
- 開発時はユーザーシークレットに退避:
dotnet user-secrets init→dotnet 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 を使って明示的に解決する癖をつけると安定します。
最小構成での動作確認レシピ
- プロジェクトを作成(例:クラスライブラリ)
dotnet new classlib -n DataContext dotnet sln add .\DataContext\DataContext.csproj - 必須パッケージを追加
dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.SqlServer dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Design - 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で上書き事故を防ぐ
トラブルが続くときの深掘りポイント
- ビルドを単体で試す:
dotnet build .\DataContext\DataContext.csproj -nologo -v:m(ビルド自体が通るか) - 詳細ログ:
dotnet ef dbcontext list --verbose --project .\DataContext\DataContext.csproj - SDK の固定:チームでズレる場合は
global.jsonを配置して SDK を固定 - Visual Studio 既定プロジェクト:CLI と食い違う挙動の温床。CLI はディレクトリ依存だと再認識
まとめ
MSB1008 は「ビルド対象が1つに定まっていない」ことのシグナルです。① 対象プロジェクトで実行する/② プロジェクトをフォルダーで分離する/③ 必要に応じて --project と --startup-project を明示する――この3点を守れば、EF Core のスキャフォールディングは .NET 9 環境でも安定して動作します。さらに出力先・名前空間・生成対象を適切に制御すれば、再生成や規模拡大にも耐える「壊れにくい」プロジェクトが出来上がります。
補足:最初に入れておくと良い NuGet パッケージ
| パッケージ | 用途 | 導入コマンド(例) |
|---|---|---|
| Microsoft.EntityFrameworkCore.SqlServer | SQL Server プロバイダー | dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.SqlServer |
| Microsoft.EntityFrameworkCore.Design | 設計時コンポーネント(必須) | dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Design |
| Microsoft.EntityFrameworkCore.Tools | VS 連携(PMC コマンド等)。CLI 中心なら必須ではない | dotnet add .\DataContext\DataContext.csproj package Microsoft.EntityFrameworkCore.Tools |
補足:スキャフォールディングの品質を高める設定例
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="<latest>" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="<latest>" PrivateAssets="all" />
</ItemGroup>
</Project>
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 開発を驚くほどスムーズにしてくれます。

コメント