Autofac故意循环依赖

时间:2018-01-11 15:09:42

标签: autofac

使用Autofac,为这种类型的循环图注册类型或声明依赖关系的正确方法是什么?

public interface IComponent
{
    void DoSomething();
}

public class AComponent: IComponent
{
    ...
}

public class BComponent: IComponent
{
    ...
}

public class CompositeComponent: IComponent
{
    public CompositeComponent(IEnumerable<IComponent> components)
    {
        this.components = components;
    }

    public void DoSomething() 
    {
        foreach(var component in components)
            component.DoSomething();
    }
}

最终目标是CompositeComponent是IComponent的默认注册,并简单地将调用传递给所有其他实现。

1 个答案:

答案 0 :(得分:1)

我正在收集问题的意图是你有一些IComponent的实现,并且你有某种CompositeComponent也实现了IComponentCompositeComponent需要除自身之外的所有已注册的IComponent个实例 ,否则会创建循环依赖项。

这一切都与我们的常见问题之一重叠:"How do I pick a service implementation by context?"

你有一些选择。按照我的个人推荐:

选项1:重新设计界面

这里实际上有两个概念 - 单个处理程序的概念以及聚合一组单独处理程序的概念。

使用不太通用的术语,您可能有一个IMessageHandler接口,然后通过所有IMessageHandler实现的集合传递消息,但那个聚合了处理程序和处理错误并确保消息只由正确的处理程序和所有那些......不是本身处理,也是消息处理程序。它是消息处理器。因此,即使界面上的方法看起来相同,您实际上也有两个不同的界面 - IMessageHandlerIMessageProcessor

回到您的通用组件术语,这意味着您现在拥有IComponent,但您还要添加IComponentManager界面。 CompositeComponent会改变以实现这一点。

public interface IComponentManager
{
    void DoSomething();
}


public class ComponentManager : IComponentManager
{
    public ComponentManager(IEnumerable<IComponent> components)
    {
        this.components = components;
    }

    public void DoSomething() 
    {
        foreach(var component in components)
            component.DoSomething();
    }
}

选项2:使用密钥服务

如果你没有(或者不能)重新设计,你可以&#34;标记&#34;哪些注册应该通过使用服务密钥对组合做出贡献。注册复合时,不要使用密钥......但是请指定构造函数所需的参数应该从键控贡献者处解析。

builder.RegisterType<AComponent>()
       .Keyed<IComponent>("contributor");
builder.RegisterType<BComponent>()
       .Keyed<IComponent>("contributor");
builder.RegisterType<CompositeComponent>()
       .As<IComponent>()
       .WithParameter(
         new ResolvedParameter(
           (pi, ctx) => pi.Name == "components",
           (pi, ctx) => ctx.ResolveKeyed<IEnumerable<IComponent>>("contributor")));

当您在未提供密钥的情况下解决IComponent时,您将获得CompositeComponent,因为它是唯一一个以此方式注册的人。{/ p>

选项3:使用Lambdas

如果您事先知道应该进入复合材料的组件集,那么您可以在lambda中构建它,而不是在整个事物中构建它。

builder.Register(ctx =>
  {
    var components = new IComponent[]
    {
      new AComponent(),
      new BComponent()
    };
    return new CompositeComponent(components);
  }).As<IComponent>();

它更具说明性,但也非常清楚。如果需要,您可以使用AComponent lambda参数解析BComponentctx的各个构造函数参数。