UnityやAutofacでDIする際に、同じ型の複数のインスタンスを注入する方法
.NETで開発をしていると、クラスを別の依存クラスの実装と分離するために、Dependency Injection(DI)を使うことは多い。 その際に、一種類の依存するインタフェースにつき、一つだけインスタンスを渡せば良いのであれば話は単純なのだが、一つのインタフェースでも、コンストラクタの異なる複数のインスタンスを渡したい、というようなケースがある。
例えば、以下のようなケースである。
IHttpClient
(実装はHttpClient
) という、何らかのHTTPリクエストを送るためのクライアントがあるHttpClient
はコンストラクタの引数としてHTTP接続の際のベースURLを受け取るとする。- このクライアントを利用する
SampleController
では、2つの異なるベースURLに接続する必要があるため、それぞれコンストラクタの異なる2つのHttpClientのインスタンスを利用したい
以下のやり方で、このような場合にも対応できる。
Unity を使っている場合
DIのライブラリにUnityを使っている場合、Unityの持つ名前付きでRegisterTypeする機能を使う。
var BaseUriA = "a.example.com"; // 1つ目のベースUri var BaseUriB = "b.example.com"; // 2つ目のベースUri var container = new UnityContainer(); container.RegisterType<IHttpClient, HttpClient>( "HttpClientA", // 一つ目の引数として任意の名前を渡す new InjectionConstructor(BaseUriA) ); container.RegisterType<IHttpClient, HttpClient>( "HttpClientB", new InjectionConstructor(BaseUriB) ); // 上でつけた名前を使って、2つのHttpClientをSampleControllerのコンストラクタに渡す container.RegisterType<SampleController>(new InjectionFactory(c => new SampleController( c.Resolve<IHttpClient>("HttpClientA"), c.Resolve<IHttpClient>("HttpClientB") ) ));
public class SampleController : ApiController { private IHttpClient HttpClientA; private IHttpClient HttpClientB; public SampleController(IHttpClient HttpClientA, IHttpClient HttpClientB) { this.HttpClientA = HttpClientA; this.HttpClientB = HttpClientB; } }
Autofac を使っている場合
Unityはもはやメンテされていないため、今後新しいプロジェクトでは利用しないことが望ましい。代替としてよく名前の上がるAutofacの場合、Named and Keyed Services
という機能で目的の挙動が実現できる。
依存を受け取る側で、IIndex<K, V>という型で複数インスタンスを受け取り、名前を指定することで目的のインスタンスを取り出す。
var BaseUriA = "a.example.com"; # 1つ目のベースUri var BaseUriB = "b.example.com"; # 2つ目のベースUri var builder = new ContainerBuilder(); builder.RegisterType<HttpClient>() .WithParameter(new TypedParameter(typeof(string), BaseUriA)) .Named<IHttpClient>("HttpClientA"); builder.RegisterType<HttpClient>() .WithParameter(new TypedParameter(typeof(string), BaseUriB)) .Named<IHttpClient>("HttpClientB");
public class SampleController : ApiController { private IIndex<HttpClient, IHttpClient> httpClients; public SampleController(IIndex<HttpClient, IHttpClient> httpClients) { this.httpClients = httpClients; } public IHttpClient GetClientA() { return httpClients["HttpClientA"]; } }