温莎对瞬态生活方式组成部分的循环依赖

时间:2013-09-30 12:07:21

标签: c# dependency-injection castle-windsor circular-dependency castle

我有一堆像这样编写的课程:

public class MyService1 {
    public MyService1(MyService1Settings settings, <service-dependent list of dependencies filled by Windsor>) { ... }
}

在温莎注册如下:

container.Register(
    ...
    Component.For<MyService1>().LifestyleTransient(),
    Component.For<MyService2>().LifestyleTransient(),
    ...
);

容器没有注册任何MyServiceXSettings类型,因此获取服务的唯一方法是从容器中解析它,如下所示:

TService service = windsorContainer.Resolve<TService>(new { settings });

问题是,根据settings对象中的参数,其中一个服务尝试使用不同的设置对象获取其类型的另一个实例。

有些事情:

public class MyService2 {
    public MyService2(MyService2Settings settings, <service-dependent list of dependencies filled by Windsor>)
    {
        this.uplink = settings.Counter == 1
            ? new AnotherUplink()
            : new RecursiveUplink(
                container.Resolve<MyService2>(new {
                    settings = new MyService2Settings(settings.Counter - 1)
                });
    }
}

这个递归依赖链是有限的(并且大约有6个实例),但是当第一个服务尝试获取另一个服务时,Windsor会抛出异常,说明它是循环依赖。

我已将所有服务宣传为具有Transient生活方式并使用自定义参数请求它们。我至少可以指定递归的最大允许深度吗?或者我错过了另一种我可以做到的方式?

另一个要求:我不能使用类型化的工厂,因为我有很多不同类型的服务,所以不希望为这些服务单独生成许多工厂接口。

2 个答案:

答案 0 :(得分:1)

  

容器没有注册任何MyServiceXSettings类型,   所以获得服务的唯一方法是从容器中解决它   这样:

您也可以在组件注册期间使用专用的SubResolver或DependsOn。

在构造函数中执行代码(而不是简单的变量赋值)是一种气味,即使使用容器也是最糟糕的:它应该永远不会在应用程序层中泄漏。

乍一看,似乎您只使用设置在构造函数中选择正确的组件:应该在CompositionRoot中使用TypedFactory或naming convention完成(您可能已注册了多个组件)对于相同的intercace,但给定的参数名称驱动组件选择)

答案 1 :(得分:0)

根据this answer,我选择了懒惰的分辨率。

/// <summary>
/// Represents single component instance producer.
/// </summary>
/// <typeparam name="TComponent">type of the component to create</typeparam>
public interface IComponentCreator<TComponent>
{
    /// <summary>
    /// Gets the created component.
    /// </summary>
    TComponent Component { get; }
}

/// <summary>
/// Creates the component only when it's first requested.
/// </summary>
/// <typeparam name="TComponent">type of the component to create</typeparam>
public class LazyCreator<TComponent> : IComponentCreator<TComponent>
{
    private readonly Func<TComponent> creatingFunction;
    private bool created;
    private TComponent component;

    public LazyCreator(Func<TComponent> creatingFunction)
    {
        this.creatingFunction = creatingFunction;
    }

    public TComponent Component
    {
        get
        {
            if (!created)
            {
                component = creatingFunction();
                created = true;
            }

            return component;
        }
    }
}

/// <summary>
/// Returns already created component.
/// </summary>
/// <typeparam name="TComponent">type of the component</typeparam>
public class ComponentContainer<TComponent> : IComponentCreator<TComponent>
{
    private readonly TComponent component;

    public ComponentContainer(TComponent component)
    {
        this.component = component;
    }

    public TComponent Component
    {
        get { return component; }
    }
}