Castle Winsdor-通过类型化工厂传递的传播依赖性

时间:2018-09-02 22:26:50

标签: c# dependency-injection inversion-of-control castle-windsor

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

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

  • 控制器是库的入口点

    public class Controller
    {
        public Settings Settings { get; }
        public Handler  Handler  { get; }
    
        public Controller(Settings settings, Handler handler)
        {
            Settings = settings;
            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

1 个答案:

答案 0 :(得分:0)

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

引用容器是不正确的做法通常是正确的。但是,像所有经验法则一样,它也有局限性。

Settings的实际可变性在您的问题中尚不清楚,因此我假设您的隐含要求(它可以以未指定的方式变化)是此处的主要驱动力。但是,回答关于“如果不正确理解Settings)的真实变异性,“您将建议什么方法” 很难

i)在当前的设计中,您需要一种或另一种方式来增强容器的行为,以满足所说明的要求,因此必须访问该容器。

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

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

(有关更多详细信息,请参见此excellent explanation of the benefits of factories in Castle Windsor