Skip to content

MS_DotNetCoreDI

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

.NET Core における DI

概要

ミドルウェア / サービス / フレームワークの
差し替えを実現し、特定のフレームワークへの依存性を減らす。

  • 明示的な依存関係の原則
    constructor 注入の DI 方式を採用している。

  • 依存性反転原則

    • 抽象化は依存関係を interface に抽出することにより行われる。
    • interface 実装をパラメタとして提供するのも、戦略設計パターンの例。

補足(.NET Core で「標準」になったことの意味): .NET Framework 時代は
DI コンテナーを自分で選んで導入するものだったが、
.NET Core 以降は Microsoft.Extensions.DependencyInjection
ランタイムに標準搭載
され、前提が変わった。

.NET Framework .NET Core / .NET
コンテナー Unity / Autofac / Ninject 等を選ぶ 標準で同梱
フレームワークとの統合 各自でアダプタを書く フレームワーク自身が DI を前提に作られている
設定 web.config(XML) Program.cs(C#)
適用範囲 Web 中心 汎用ホスト(コンソール、Worker、MAUI…)

「標準がある」ことの本質的な効果は、
ライブラリ側が IServiceCollection を前提に API を提供できる
ようになった点にある。

// どのライブラリも、この形で拡張メソッドを提供する
builder.Services.AddDbContext<AppDbContext>(...);   // EF Core
builder.Services.AddHttpClient<IApiClient, ApiClient>();
builder.Services.AddAuthentication().AddJwtBearer();

原文の言う「ミドルウェア / サービス / フレームワークの差し替え」は、
拡張メソッドによる登録という共通の作法として結実した、と言える。

詳細

構成

以下のように、インジェクションを構成できる。

  • Public constructor

    • 1 つの Public constructor(複数の Public constructor は例外)
    • 依存関係の挿入によって提供されない引数は、既定値のサポートが必要。
  • 抽象化は依存関係を interface に抽出

補足(この制約の実際): 「Public constructor が 1 つ」という制約は、
標準コンテナーのあいまいさを排除するための設計である。

// ✗ 解決できない:どちらを使うべきか決められない
public class Foo
{
    public Foo(IA a) { }
    public Foo(IA a, IB b) { }
}
// → InvalidOperationException:
//    Multiple constructors accepting all given argument types...

正確には、「解決可能な引数を最も多く持つコンストラクタが
一意に決まらない場合」に例外
になる
(どちらか一方の引数集合がもう一方を包含していれば選ばれる)。

Autofac 等のサードパーティ コンテナーは
より柔軟な選択規則を持つが、
コンストラクタは 1 つに絞るのが読みやすく、事故も少ない

「既定値のサポート」については、
DI で解決しない引数は既定値を持たせるか、
ActivatorUtilities で明示的に渡す

// 一部の引数だけ手で渡す
var foo = ActivatorUtilities.CreateInstance<Foo>(sp, "手渡しの値");

対象

Program

補足(原文が空欄のため補う): 原文はここで力尽きているので、
登録と解決の実際を補っておく。

① 登録(Program.cs = 合成ルート)

var builder = Host.CreateApplicationBuilder(args);

// ライフタイムを選んで登録する([DI](MS_DI) の「コンテナー」節を参照)
builder.Services.AddSingleton<IClock, SystemClock>();
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
builder.Services.AddTransient<IMailer, SmtpMailer>();

// 設定のバインド(オプション パターン)
builder.Services.Configure<MyOptions>(
    builder.Configuration.GetSection("MyOptions"));

// ホステッド サービス(常駐処理)
builder.Services.AddHostedService<Worker>();

var host = builder.Build();
await host.RunAsync();

② 解決(constructor 注入)

public sealed class Worker(
    IOrderRepository repo,
    IOptions<MyOptions> options,
    ILogger<Worker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken ct) { ... }
}

要点:

  • ILogger<T>IOptions<T> は登録不要(ホストが既定で登録する)
  • IServiceProvider を注入して GetService を呼ぶのは避ける
    (サービス ロケータになる。IoC 参照)
  • AddHostedService は Singleton として登録される。
    中で ScopedDbContext 等)を使うなら
    IServiceScopeFactory でスコープを作る

補足(標準コンテナーで足りない場合): 標準コンテナーは
意図的に機能を絞っている。次の機能は標準では提供されない

機能 標準 代替
プロパティ注入 × Autofac / 手動
名前付き登録 △(.NET 8 で キー付きサービスが追加) [FromKeyedServices]
インターセプト(AOP) × Castle DynamicProxy(AOP
自動登録(アセンブリ走査) × Scrutor
デコレーター × Scrutor(Decorate<T,U>()

.NET 8 の キー付きサービスは、
「同じインターフェイスの実装を用途で使い分ける」という
従来からの要望に応えたものである。

builder.Services.AddKeyedSingleton<ICache, MemoryCache>("fast");
builder.Services.AddKeyedSingleton<ICache, RedisCache>("shared");

public class Svc([FromKeyedServices("shared")] ICache cache) { }

コンテナーを差し替える場合も、
IServiceCollection への登録は標準の作法のままでよい
UseServiceProviderFactory で最後に差し替える)ため、
まず標準で始めて、必要になってから移るのが安全である。

補足(.NET Framework からの移行時の注意): 既存アプリを
.NET Coreへの移行 する際、
DI 周りで問題になりやすいのは次の点である。

移行元の実装 移行時の対応
静的クラス / シングルトン(Foo.Instance インターフェイス化して AddSingleton
HttpContext.Current IHttpContextAccessor を注入
ConfigurationManager.AppSettings IConfiguration / オプション パターン
サービス ロケータ constructor 注入に段階的に置換
Unity / Autofac 標準に寄せるか、Autofac を .NET Core 版に更新

なお、Microsoft.Extensions.DependencyInjection
netstandard2.0 対応のため、
.NET Framework のまま先に DI を導入しておくことができる。
移行の足場作りとして有効な手順である。

参考

Microsoft Learn

DevIQ > Posts

Principles

Patterns

Qiita


Tags: 移行, プログラミング, .NET開発, .NET Core, ASP.NET, ASP.NET MVC

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally