2017-01-05 18 views
0

特定のインターフェイスとしてクラスを登録しようとしていて、明示的なクラスをコンストラクタのパラメータとして使用しようとしています。問題はそれらがすべて私の現在の登録がAutofacの "Circular component dependency detected"で終わる同じインターフェースを実装していることです。ここでAutofacによるネストされた登録

は、私が得たものである:

public interface IWorker 
{ 
    void DoWork(); 
} 
public class FirstLineWorker : IWorker 
{ 
    public void DoWork() 
    { 
     //Do some work 
    } 
} 
public class SecondLineWorker : IWorker 
{ 
    public void DoWork() 
    { 
     //Do some morework 
    } 
} 
public class SequentialWorker : IWorker 
{ 
    private readonly IList<IWorker> _workers; 

    public SequentialWorker(params IWorker[] workers) 
    { 
     _workers = workers; 
    } 

    public void DoWork() 
    { 
     foreach (var worker in _workers) 
     { 
      worker.DoWork(); 
     } 
    } 
} 

はどうAutofacで私はプログラム的に明示的なパラメータFirstLineWorkerSecondLineWorkerSequentialWorkerとしてIWorkerを登録することができますか?

た円形reigstrationで終わった初の試み:

var builder = new ContainerBuilder(); 
builder 
    .RegisterType<FirstLineWorker>() 
    .Named<IWorker>(typeof(FirstLineWorker).Name) 
    .InstancePerDependency(); 
builder 
    .RegisterType<SecondLineWorker>() 
    .Named<IWorker>(typeof(SecondLineWorker).Name) 
    .InstancePerDependency(); 
builder.RegisterType<SequentialWorker>() 
    .As<IWorker>() 
    .WithParameter(ResolvedParameter.ForNamed<IWorker>(typeof(FirstLineWorker).Name)) 
    .WithParameter(ResolvedParameter.ForNamed<IWorker>(typeof(SecondLineWorker).Name)) 
    .InstancePerDependency(); 

任意のアイデア?

答えて

1

少なくとも2つの方法があります。まず、次に

public interface IWorkExecutor : IWorker { } 

のように、IWorkerから派生した新しい、空のインターフェイスを、作成IWorkerとして「正常な」労働者を登録し、「実行者」IWorkExecutor

builder.RegisterType<Worker1>().As<IWorker>(); 
builder.RegisterType<Worker2>().As<IWorker>(); 
builder.RegisterType<SequentialWorker>().As<IWorkExecutor>(); 

第二のアプローチと全く同様であろうあなたが既に持っているコードに:

containerBuilder.RegisterType<Worker1>().Named<IWorker>(nameof(Worker1)); 
containerBuilder.RegisterType<Worker2>().Named<IWorker>(nameof(Worker2)); 
containerBuilder.RegisterType<SequentialWorker>().As<IWorker>() 
    .WithParameter(new ResolvedParameter(
     (pi, cc) => pi.Name == "workers", 
     (pi, cc) => new [] { cc.ResolveNamed<IWorker>(nameof(Worker1)), cc.ResolveNamed<IWorker>(nameof(Worker2)) })); 

あなたはparams IWorker[] workersのために、単一配列パラメータを渡す必要がありSequentialWorker

BTW InstancePerDependency()は、タイプを登録するときのデフォルトの有効期間スコープであるため、実際には必要ありません。

+0

を解決するときにToArray()を実行する必要性を回避して、あなたはIEnumerableを使用することができます - それは、よりクリーンで読みやすいです。また、実際には、Worker1とWorker2の役割は、同じインタフェースを使用している場合でも、SequentialWorkerとは異なります – tdragon

1

これは部分的にthisの複合パターンに関する質問(ここで実装しているもの)によって回答されています。

ソリューションは、共通の名前を持つすべてのサービスを登録し、複合の登録以内という名前のコレクションを解決することです:

builder 
    .RegisterType<FirstLineWorker>() 
    .Named<IWorker>("worker") 
    .InstancePerDependency(); 
builder 
    .RegisterType<SecondLineWorker>() 
    .Named<IWorker>("worker") 
    .InstancePerDependency(); 

のいずれか

builder 
    .Register<IWorker>(c => new SequentialWorker(
      c.ResolveNamed<IEnumerable<IWorker>>("worker").ToArray())) 
    .As<IWorker>() 
    .InstancePerDependency(); 

または

builder.RegisterType<SequentialWorker>() 
    .As<IWorker>() 
    .WithParameter(new ResolvedParameter(
     (pi, cc) => pi.Name == "workers", 
     (pi, cc) => cc.ResolveNamed<IEnumerable<IWorker>>("worker").ToArray())); 

paramsは実際には再ありませんquiredそれは最初のアプローチが優れているIMHO "worker"

public SequentialWorker(IEnumerable<IWorker> workers) 
{ 
    _workers = workers; 
} 
関連する問題