• システム開発に関わる内容をざっくりと書いていく

DI「Unable to resolve service for type」の原因と対策

ASP.NET Core / .NET の DI でよく出る Unable to resolve service for type 'XXX' while attempting to activate 'YYY'。登録漏れか、寿命の取り違えか、実装型の取り違えかがほとんど。

この記事では原因の型切り分け手順をざっくりまとめる。


先に結論

  • メッセージの for type 'A' while activating 'B' は、「B を作ろうとして依存 A がコンテナに無い」
  • 原因の大半は AddXxx し忘れ / インターフェースと実装の取り違え / 別コンテナ・別 Host で登録
  • まず Program.cs(または Startup)の登録と、例外の A / B の型名を突き合わせる
  • 開発時は ValidateOnBuild / ValidateScopes を有効にすると起動時に気づきやすい

エラーの読み方

Unable to resolve service for type 'IOrderRepository'
while attempting to activate 'OrdersController'.
  • for type … 足りない依存(上なら IOrderRepository
  • while activating … それを欲しがっている側(上ならコントローラ)
  • 内側にさらに同じ形式が続くこともある(依存の依存が未登録)

よくある原因

1. そもそも登録していない

// 足りない例
// builder.Services.AddScoped<IOrderRepository, OrderRepository>();

public class OrdersController(IOrderRepository repo) { }

2. インターフェースで注入しているのに具象だけ登録

// NG: 具象だけ
builder.Services.AddScoped<OrderRepository>();
// コンストラクタは IOrderRepository → 解決できない

// OK
builder.Services.AddScoped<IOrderRepository, OrderRepository>();

3. 拡張メソッドを呼び忘れ(AddXxx)

AddDbContextAddHttpClientAddAuthentication など「セットで登録する系」を呼んでいないと、内側の型が足りない、となりやすい。

4. テスト / Worker / 別 Host で登録が違う

  • Web では動くが WebApplicationFactory で落ちる → テスト側の ConfigureTestServices で上書きしすぎ / 消しすぎ
  • BackgroundService から GetRequiredService → スコープ未作成や登録漏れ
  • 複数プロジェクトで DI 登録が分散 → 起動プロジェクト側で拡張メソッドを呼んでいない

5. オープンジェネリック・オプション周り

  • IOptions<T> が欲しいのに Configure<T> していない
  • ILogger<T> は通常フレームワークが登録済み。自前コンテナを組むと足りないことがある

切り分け手順

  1. 例外文面から「足りない型 A」と「要求側 B」を控える
  2. Program.cs / AddInfrastructure() 等で A の登録を検索
  3. 登録が「具象のみ / インターフェースのみ / 別アセンブリ」のどれか確認
  4. B のコンストラクタ引数と登録キー(インターフェース名)が一致するか確認
  5. テストや Worker なら、その Host の IServiceCollection を別経路で見ていないか確認

予防

builder.Host.UseDefaultServiceProvider(o =>
{
    o.ValidateScopes = true;
    o.ValidateOnBuild = true; // 起動時に解決できるかざっくり検証
});
  • レイヤごとの AddXxx(this IServiceCollection) に登録を集約し、起動で 1 回呼ぶ
  • インターフェース注入なら必ず AddScoped<IFoo, Foo>() 形式で揃える

関連: DI のスコープとライフサイクル

ざっくりまとめ

  • 「A が無いのに B を作ろうとした」エラー
  • まず登録漏れと I/具象の不一致を疑う
  • Host が違う(テスト・Worker)も頻出
  • ValidateOnBuild で起動時に寄せる

関連記事