同じインターフェースから派生したサービスがあります。
public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService { }
public class ServiceC : IService { }
通常、他の IoC コンテナーでは、Unity
具体的な実装を区別するものによって登録できますKey
。
ASP.NET Core では、これらのサービスを登録し、実行時に何らかのキーに基づいて解決するにはどうすればよいですか?
通常は具体的な実装を区別するために使用されるまたはパラメータをAdd
取る Service メソッドは見当たりません。key
name
public void ConfigureServices(IServiceCollection services)
{
// How do I register services of the same interface?
}
public MyController:Controller
{
public void DoSomething(string key)
{
// How do I resolve the service by key?
}
}
ここでのオプションは Factory パターンのみでしょうか?
更新1
私は記事を読みましたこここれは、複数の具体的な実装がある場合に、ファクトリ パターンを使用してサービス インスタンスを取得する方法を示しています。ただし、これはまだ完全なソリューションではありません。メソッドを呼び出すときに_serviceProvider.GetService()
、コンストラクターにデータを挿入できません。
たとえば、次のことを考えてみましょう。
public class ServiceA : IService
{
private string _efConnectionString;
ServiceA(string efconnectionString)
{
_efConnecttionString = efConnectionString;
}
}
public class ServiceB : IService
{
private string _mongoConnectionString;
public ServiceB(string mongoConnectionString)
{
_mongoConnectionString = mongoConnectionString;
}
}
public class ServiceC : IService
{
private string _someOtherConnectionString
public ServiceC(string someOtherConnectionString)
{
_someOtherConnectionString = someOtherConnectionString;
}
}
適切な接続文字列を挿入するにはどうすればよいでしょうか_serviceProvider.GetService()
? Unityや他のIoCライブラリでは、型登録時にこれを行うことができます。オプションただし、そのためにはすべての設定を挿入する必要があります。特定の接続文字列をサービスに挿入することはできません。
また、他のコンテナー (Unity を含む) の使用を避けようとしていることに注意してください。そうしないと、他のすべて (コントローラーなど) も新しいコンテナーに登録する必要があるためです。
また、ファクトリパターンを使用してサービスインスタンスを作成すると、クライアントの依存関係の数が増えるため、DIPに反します。詳細はこちら。
したがって、ASP.NET Core のデフォルトの DI には次の 2 つの点が欠けていると思います。
- キーを使用してインスタンスを登録する機能
- 登録時にコンストラクタに静的データを挿入する機能
ベストアンサー1
Func
このような状況に陥ったとき、私は を使用して簡単な回避策を実行しました。
まず共有デリゲートを宣言します。
public delegate IService ServiceResolver(string key);
次に、 でStartup.cs
、複数の具体的な登録とそれらのタイプの手動マッピングを設定します。
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
switch (key)
{
case "A":
return serviceProvider.GetService<ServiceA>();
case "B":
return serviceProvider.GetService<ServiceB>();
case "C":
return serviceProvider.GetService<ServiceC>();
default:
throw new KeyNotFoundException(); // or maybe return null, up to you
}
});
そして、DI に登録された任意のクラスからそれを使用します。
public class Consumer
{
private readonly IService _aService;
public Consumer(ServiceResolver serviceAccessor)
{
_aService = serviceAccessor("A");
}
public void UseServiceA()
{
_aService.DoTheThing();
}
}
この例では、簡潔にするため、また OP が特にこのケースを要求していたため、解決のキーは文字列であることに留意してください。
ただし、通常は巨大な n-case スイッチによってコードが劣化することは望ましくないため、任意のカスタム解決タイプをキーとして使用できます。アプリのスケーリング方法によって異なります。