Castle Windsor - 通过打字工厂传递的传播依赖性

问题描述 投票:1回答:1

我目前熟悉IoC,并且在使用IoC原理重构现有库时遇到困难。

假设我有三个类:Controller,Handler和Settings以及Controller的类型工厂。

  • Controller是图书馆的入口点 public class Controller { public Settings Settings { get; } public Handler Handler { get; } public Controller(Settings settings, Handler handler) { Settings = settings; Handler = handler; } }
  • Handler是Controller的依赖 public class Handler { public Settings Settings { get; } public Handler(Settings settings) { Settings = settings; } }
  • 设置是一个包含一些库范围设置的类' public class Settings { public int Revision { get; set; } }
  • IControllerFactory是一个类型化的工厂 public interface IControllerFactory { Controller Create(Settings settings); }

我想初始化一个库。为简单起见,所有示例代码都在单个Main方法中。在实际应用程序中,Controller类的使用者无权访问容器。

static void Main(string[] args)
{
    //create container and register components
    var container = new WindsorContainer();
    container.AddFacility<TypedFactoryFacility>();
    container.Register(
        Component.For<Controller>().LifestyleTransient(),
        Component.For<Settings>().LifestyleBoundTo<Controller>(),
        Component.For<Handler>().LifestyleBoundTo<Controller>()
    );
    container.Register(
        Component.For<IControllerFactory>().AsFactory()
    );

    //in real application, factory is a dependency of a library consumer class 
    //which has no access to container
    var controllerFactory = container.Resolve<IControllerFactory>();

    //create Controller instance with Revision setting set to 100
    var settings = new Settings()
    {
        Revision = 100
    };
    var controller = controllerFactory.Create(settings);

    //check revision value for controller and handler
    Console.WriteLine("Controller's setting revision: " + controller.Settings.Revision);         //Controller's setting revision: 100
    Console.WriteLine("Handler's setting revision: "    + controller.Handler.Settings.Revision); //Handler's setting revision: 0
    Console.ReadKey();
}

运行此示例将输出以下内容:

Controller's setting revision: 100
Handler's setting revision: 0

如您所见,作为参数传递给工厂的Settings实例正确传递给Controller构造函数,但不传播到Controller的依赖项(即Handler构造函数)。我找不到任何关于它是否是预期行为的信息。


如果类型化工厂的参数实际上是不可传播的,那么在我的情况下你会建议采用什么方法?创建自定义范围是一个,但不能满足我的需求,因为它需要访问容器,据我所知,这被认为是一种不好的做法。

static void Main(string[] args)
{
    //create container and register components
    var container = new WindsorContainer();
    container.AddFacility<TypedFactoryFacility>();
    container.Register(
        Component.For<Controller>().LifestyleScoped(),
        Component.For<Settings>().LifestyleScoped(),
        Component.For<Handler>().LifestyleBoundTo<Controller>()
    );

    //creating scope means passing container around
    using (container.BeginScope()) 
    {
        //create instance of controller
        var settings = container.Resolve<Settings>();
        settings.Revision = 100;
        var controller = container.Resolve<Controller>();

        //check revision value for controller and handler
        Console.WriteLine("Controller's setting revision: " + controller.Settings.Revision);         //Controller's setting revision: 100
        Console.WriteLine("Handler's setting revision: "    + controller.Handler.Settings.Revision); //Handler's setting revision: 100
    }

    Console.ReadKey();
}

运行此示例会得到所需的结果:

Controller's setting revision: 100
Handler's setting revision: 100
c# dependency-injection inversion-of-control castle-windsor
1个回答
0
投票

传递给工厂的参数不会添加到容器中 - 它们仅用于满足所需服务的依赖关系。如果您希望将这些参数传递给其他依赖项,您必须自己负责这样做。

引用容器的一般经验法则是不好的做法是正确的。然而,像所有的经验法则一样,它有限制。

Settings的实际变化在你的问题中不明确,所以我假设你的隐含要求,它可以以一种未指明的方式变化,是这里的关键驱动因素。但是,回答你关于“你会建议什么方法”的问题很难没有正确理解Settings的实际变化)

i)根据您当前的设计,您需要以某种方式增加容器的行为以满足您的要求,因此您必须访问容器。

正如您已经强调的那样,一种解决方案是使用自定义范围。这为控制对象生命周期提供了很大的灵活性,但确实以相当危险的方式泄漏了容器引用。为避免这种情况,您可以将其封装起来以防止容器泄漏,最终会得到一个自定义工厂和合作伙伴范围类(就客户而言,它可能只是一个IDisposable)。在这种情况下,我认为这是容器基线功能的增强,因此经验法则不适用。

ii)另一种方法是重新考虑你的设计,并引入一些间接参考Settings。即,而不是HandlerController依赖Settings,他们可能依赖SettingsProvider。然后可以将SettingsProvider作为单例添加到容器中,并且可以完全独立于容器来管理关于如何访问当前适用的Settings的所需逻辑。

(详情请见excellent explanation of the benefits of factories in Castle Windsor

© www.soinside.com 2019 - 2024. All rights reserved.