C# 依存注入 ライフタイム (AddTransient, AddScoped, AddSingleton) の違い:コード例による解説
AddTransient (トランジエント)
- トランジエントは 毎回要求されたときに新しいインスタンス が生成されます。
- 同じクラスに対して複数回要求を出しても、常に新しいインスタンス が返されます。
- データなどの状態を保持する必要がなく、要求ごとに独立した処理を行うサービスに適しています。
AddScoped (スコープド)
- スコープドは HTTP リクエストごとに 1 つのインスタンス が生成されます。
- 同じ HTTP リクエスト内であれば、同じサービスに対して何度要求を出しても同じインスタンス が返されます。
- リクエスト内で状態を保持する必要があるサービス (e.g. ショッピングカート) に適しています。
AddSingleton (シングルトン)
- シングルトンは アプリケーション起動時に 1 つのインスタンス が生成され、アプリケーションが終了するまで それが再利用 されます。
- アプリケーション内で 常に同じインスタンス が返されます。
- キャッシュや設定情報など、アプリケーション全体で共有すべきデータを持つサービスに適しています。
ライフタイム | 生成されるインスタンス数 | 適用例 |
---|---|---|
AddTransient | 毎回生成 | ロガー (Logger) など、状態保持を必要としないサービス |
AddScoped | HTTP リクエストごとに 1 つ | ショッピングカートなど、リクエスト内で状態を保持するサービス |
AddSingleton | アプリケーション起動時に 1 つ | キャッシュや設定情報など、アプリケーション全体で共有するデータ |
注意
- スコープドは ASP.NET Core での動作であり、.NET Core のコンソールアプリケーションなどでは異なる動作をする可能性があります。
C# 依存注入 ライフタイム (AddTransient, AddScoped, AddSingleton) の違い:コード例による解説
依存注入とは?
依存注入 (Dependency Injection) とは、オブジェクト間の依存関係をコードの外部から注入することで、オブジェクトの結合度を下げ、テストの容易性や再利用性を高める設計手法です。
ライフタイムの違い
C# の依存注入では、サービスのライフタイム (生存期間) を AddTransient
, AddScoped
, AddSingleton
の3種類で指定できます。
- 毎回新しいインスタンス が生成されます。
- 例:
public interface IMyService
{
// ...
}
public class MyService : IMyService
{
// ...
}
// Startup.cs
services.AddTransient<IMyService, MyService>();
- 特徴:
- 毎回新しいインスタンスが生成されるため、状態を保持する必要がないサービスに適しています。
- 例えば、ロガーや一意な ID を生成するサービスなどが挙げられます。
public interface IShoppingCart
{
// ...
}
public class ShoppingCart : IShoppingCart
{
// ...
}
// Startup.cs
services.AddScoped<IShoppingCart, ShoppingCart>();
- 特徴:
- HTTP リクエスト内で状態を保持する必要があるサービスに適しています。
- 例えば、ショッピングカートやデータベースのコンテキストなどが挙げられます。
public interface IConfigurationService
{
// ...
}
public class ConfigurationService : IConfigurationService
{
// ...
}
// Startup.cs
services.AddSingleton<IConfigurationService, ConfigurationService>();
- 特徴:
- アプリケーション全体で共有する必要があるサービスに適しています。
- 例えば、設定情報やキャッシュなどが挙げられます。
コード例:ライフタイムの違いを実感する
public class HomeController : Controller
{
private readonly IMyService _myService;
private readonly IShoppingCart _shoppingCart;
private readonly IConfigurationService _configurationService;
public HomeController(IMyService myService, IShoppingCart shoppingCart, IConfigurationService configurationService)
{
_myService = myService;
_shoppingCart = shoppingCart;
_configurationService = configurationService;
}
public IActionResult Index()
{
// 毎回新しいインスタンスが生成される
var myService1 = _myService;
var myService2 = _myService;
// HTTP リクエスト内で同じインスタンスが使用される
var shoppingCart1 = _shoppingCart;
var shoppingCart2 = _shoppingCart;
// アプリケーション全体で同じインスタンスが使用される
var configurationService1 = _configurationService;
var configurationService2 = _configurationService;
// ...
}
}
- AddTransient: 毎回新しいインスタンスが必要な場合
- AddScoped: HTTP リクエスト内で状態を保持する必要がある場合
- AddSingleton: アプリケーション全体で共有する必要がある場合
適切なライフタイムを選択することで、アプリケーションのパフォーマンスや保守性を向上させることができます。
- ライフタイムの選択は、サービスの役割やアプリケーションの要件によって異なります。
- 誤ったライフタイムを選択すると、意図しない動作やバグが発生する可能性があります。
- DI コンテナは、サービスのライフタイムを管理するだけでなく、依存関係の解決も自動化します。
キーワード: C#, ASP.NET Core, 依存注入, ライフタイム, AddTransient, AddScoped, AddSingleton, コード例
- 上記のコード例は簡略化されており、実際のアプリケーションではより複雑なシナリオが考えられます。
- 依存注入の概念は、C# に限らず、他のプログラミング言語でも共通して使用されます。
従来のインスタンス生成方法との比較
依存注入の登場以前は、クラス内で直接インスタンスを生成する手法が一般的でした。
// 従来の方法
public class MyClass
{
private readonly MyService _myService = new MyService();
// ...
}
この方法では、クラス間の結合度が高くなり、テストが難しくなるなどの問題がありました。
依存注入のメリット
- 結合度の低下: クラス間の依存関係を明確にし、テストしやすいコードになります。
- 再利用性の向上: 異なるコンテキストで同じサービスを再利用できます。
- 保守性の向上: コードの変更が容易になります。
AddTransient, AddScoped, AddSingleton 以外の方法
厳密に言えば、AddTransient, AddScoped, AddSingleton 以外の方法でライフタイムを管理することはあまり一般的ではありません。これらの3つの方法が、ASP.NET Core の依存注入において最もよく使用される標準的な方法です。
しかし、より高度なシナリオや特殊な要件に対応するために、以下のような手法が考えられます。
カスタム ライフタイム マネージャーの作成
- IServiceProvider を継承し、独自のライフタイム管理ロジックを実装します。
- 複雑なライフタイム管理が必要な場合に有効です。
ファクトリー パターン
- サービスのインスタンス生成をファクトリークラスに委譲します。
- より柔軟なインスタンス生成が可能になります。
オブジェクト プール
- オブジェクトをプールして再利用することで、パフォーマンスを向上させます。
- 短寿命のオブジェクトを頻繁に生成する場合に有効です。
AddTransient, AddScoped, AddSingleton は、ASP.NET Core の依存注入において、最も一般的なライフタイム管理の方法です。これらの3つの方法で、ほとんどのシナリオに対応できます。
しかし、より高度なシナリオや特殊な要件がある場合は、カスタム ライフタイム マネージャーの作成やファクトリーパターンなどの手法を検討することも可能です。
重要なのは、アプリケーションの要件に合わせて適切なライフタイムを選択し、依存関係を適切に管理することです。
- DI コンテナ: Autofac, Ninject などのサードパーティ製のDIコンテナを使用することで、より柔軟な依存注入が可能になる場合があります。
- ライフタイムスコープ: ASP.NET Core では、リクエストスコープ、トランザクションスコープなど、さまざまなスコープが定義されています。
注意: カスタム ライフタイム マネージャーやファクトリーパターンを実装する場合は、慎重に設計する必要があります。誤った実装を行うと、パフォーマンス低下やメモリリークなどの問題が発生する可能性があります。
キーワード: C#, ASP.NET Core, 依存注入, ライフタイム, AddTransient, AddScoped, AddSingleton, 代替方法, カスタム ライフタイム マネージャー, ファクトリー パターン
- 依存注入は、単なるインスタンス生成の仕組みだけでなく、アプリケーションの設計全体に影響を与える重要な概念です。
- 依存注入を効果的に活用することで、より保守性の高い、拡張性の高いアプリケーションを構築することができます。
c# asp.net-core .net-core