web-dev-qa-db-ja.com

Unityを使用して、名前付き依存関係をコンストラクターに挿入するにはどうすればよいですか?

次のコードでIRespositoryを(名前を付けて)2回登録しています。

// Setup the Client Repository
IOC.Container.RegisterType<ClientEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Client", new InjectionConstructor(typeof(ClientEntities)));

// Setup the Customer Repository
IOC.Container.RegisterType<CustomerEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Customer", new InjectionConstructor(typeof(CustomerEntities)));

IOC.Container.RegisterType<IClientModel, ClientModel>();
IOC.Container.RegisterType<ICustomerModel, CustomerModel>();

しかし、(IRepositoryを使用するために)これを解決したいときは、次のように手動で解決する必要があります。

public ClientModel(IUnityContainer container)
{
   this.dataAccess = container.Resolve<IRepository>(Client);

   .....
}

私がやりたいのは、コンストラクタで解決することです(IUnityContainerのように)。解決する名前付きの型を指定する方法が必要です。

このようなもの:(注:実際のコードではありません)

public ClientModel([NamedDependancy("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}

偽のコードを機能させる方法はありますか?

66
Vaccano

API、属性、または構成ファイルを使用して、名前の有無に関係なく依存関係を構成できます。上記でXMLについて言及しなかったので、APIを使用していると仮定します。

名前付き依存関係を解決するようにコンテナに指示するには、InjectionParameterオブジェクトを使用する必要があります。 ClientModelの例では、次を実行します。

container.RegisterType<IClientModel, ClientModel>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<IRepository>("Client") // Resolve parameter of type IRepository using name "Client"
    )
);

これは、コンテナに「ClientModelを解決するとき、単一のIRepositoryパラメータを取るコンストラクタを呼び出します。そのパラメータを解決するとき、タイプに加えて「Client」という名前で解決します。」

属性を使用したい場合、サンプルはほとんど機能します。属性名を変更するだけです。

public ClientModel([Dependency("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}
85
Chris Tavares

これは非常に遅い応答ですが、質問は引き続きGoogleに表示されます。

とにかく、5年後...

私は非常にシンプルなアプローチをしています。通常、「名前付き依存関係」を使用する必要があるのは、何らかの戦略パターンを実装しようとしているためです。その場合、UnityとStrategyResolverと呼ばれるコードの残りの部分との間に間接的なレベルを作成するだけで、Unityに直接依存しません。

public class StrategyResolver : IStrategyResolver
{
    private IUnityContainer container;

    public StrategyResolver(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public T Resolve<T>(string namedStrategy)
    {
        return this.container.Resolve<T>(namedStrategy);
    }
}

使用法:

public class SomeClass: ISomeInterface
{
    private IStrategyResolver strategyResolver;

    public SomeClass(IStrategyResolver stratResolver)
    {
        this.strategyResolver = stratResolver;
    }

    public void Process(SomeDto dto)
    {
        IActionHandler actionHanlder = this.strategyResolver.Resolve<IActionHandler>(dto.SomeProperty);
        actionHanlder.Handle(dto);
    }
}

登録:

container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();

さて、これのいいところは、将来新しい戦略を追加するときに、StrategyResolverに再び触れる必要がないことです。

とても簡単です。非常にきれいで、Unityへの依存を最小限に抑えました。 StrategyResolverに触れるのは、起こりそうにないコンテナテクノロジーを変更することだけです。

お役に立てれば!

編集:サービスのコンストラクターでDependency属性を使用すると、実際にはUnityに強い依存関係があるため、受け入れられた答えがあまり好きではありません。 Dependency属性は、Unityライブラリの一部です。その時点で、どこでもIUnityContainer依存関係を渡すこともできます。

私は、サービスクラスを、外部ライブラリに全面的に依存するのではなく、完全に所有するオブジェクトに依存させることを好みます。また、Dependency属性を使用すると、コンストラクターの署名が簡潔で単純になります。

さらに、この手法により、コンストラクター、アプリケーション構成ファイルで名前付き依存関係をハードコーディングしたり、設計時に使用する名前付き依存関係を知る必要があるInjectionParameterを使用したりすることなく、実行時に名前付き依存関係を解決できます時間。

編集(2016-09-19):疑問に思われるかもしれませんが、IUnityContainerコンストラクター署名に示されているように、StrategyResolverを依存関係として要求するときに、コンテナーは自分自身を渡すことを知っています。

編集(2018-10-20):ファクトリを使用するだけの別の方法があります:

public class SomeStrategyFactory : ISomeStrategyFactory
{
    private IStrategy _stratA;
    private IStrategy _stratB;

    public SomeFactory(IStrategyA stratA, IStrategyB stratB)
    {
        _stratA = stratA;
        _stratB = stratB;
    }

    public IStrategy GetStrategy(string namedStrategy){
        if (namedStrategy == "A") return _stratA;
        if (namedStrategy == "B") return _stratB;
    }
}

public interface IStrategy {
    void Execute();
}

public interface IStrategyA : IStrategy {}

public interface IStrategyB : IStrategy {}

public class StrategyA : IStrategyA {
    public void Execute(){}
}

public class StrategyB : IStrategyB {
    public void Execute() {}
}

使用法:

public class SomeClass : ISomeClass
{
    public SomeClass(ISomeStrategyFactory strategyFactory){

        IStrategy strat = strategyFactory.GetStrategy("HelloStrategy");
        strat.Execute();

    }
}

登録:

container.RegisterType<ISomeStrategyFactory, SomeStrategyFactory>();
container.RegisterType<IStrategyA, StrategyA>();
container.RegisterType<IStrategyB, StrategyB>();
container.RegisterType<ISomeClass, SomeClass>();

この2番目の提案は同じものですが、工場設計パターンを使用しています。

お役に立てれば!

24
TchiYuan

ParameterOverridesを使用できるはずです

var repository = IOC.Container.Resolve<IRepository>("Client");
var clientModel = IOC.Container.Resolve<ClientModel>(new ParameterOverrides<ClientModel> { {"dataAccess", repository } } );

編集:なぜあなたはUnityContainerを回しているのか分かりません-個人的に、私たちはコンストラクタ自体に依存関係を注入します(これは私が見たものから「正常」です)。ただし、RegisterTypeメソッドとResolveメソッドで名前を指定できます。

IOC.Container.RegisterType<IRepository, GenericRepository>("Client");
IOC.Container.Resolve<IRepository>("Client");

その名前に対して登録したタイプが表示されます。

3
Kyle W

これをしないでください-class ClientRepository : GenericRepository { }およびタイプシステムを利用します。

0
mcintyre321