【问题标题】:How can I pass an argument down the resolution pipeline in Castle Windsor?如何在温莎城堡的解决管道中传递参数?
【发布时间】:2016-06-08 05:46:21
【问题描述】:

我有一个抽象工厂(由 Windsor 使用 .AsFactory() 管理),它解析的组件之一需要特定的依赖项。通常,这不是问题,因为 Windsor 可以查看为该抽象注册了哪个实现并提供它。但是,该依赖项有多种实现,我想使用传递给顶级工厂的 same argument 选择一个。

例如:

public interface IAbstractFactory
{
    IComponent Create(int argument);
}

public class MyComponent : IComponent
{
    public MyComponent(IDependency dependency) { }
}

public interface IDependencyFactory
{
    IDependency Create(int argument); // same argument as above
}

我想创建注入到MyComponentIDependency 实例,使用温莎工厂,其参数与传递给IAbstractFactory 的参数相同。我希望 Windsor 使用 IDependencyFactory 自动解析 IDependency 并将其提供给 MyComponent,因为我不想在调用时将参数传递给 MyComponentIDependency选择的具体实现需要基于传入IAbstractFactory.Create(argument)的相同参数来完成。此过程可能需要在整个解析管道中继续,使用相同的argument 选择更多组件。

选择实现的特定逻辑将查找特定类属性的存在并尝试匹配其上的值,如果找不到则默认为 Windsor 的默认选择行为。

我怀疑有一种方法可以使用自定义处理程序或ISubDependencyResolver 的实现来做到这一点,但我无法将这些部分组合在一起。

有没有一种合理的方法来解决这个问题,只使用一些更高级的注册和组件选择功能?

【问题讨论】:

    标签: c# dependency-injection castle-windsor typed-factory-facility


    【解决方案1】:

    你能解释一下大局吗? IComponents 或 IDependency 是如何使用的以及由谁使用?

    我要求这样做的原因如下:

    1. 如果您事先知道 int 参数和所需 IDependency 之间的映射,并且它映射为单个 IDependency,那么我不会添加工厂,而是直接在安装程序中声明此依赖项:

          container.Register(
              Component.For<IDependency>()
                  .ImplementedBy<SomeDependency>()
                  .Named("SomeDependency"),
              Component.For<IComponent>()
                  .ImplementedBy<MyComponent>()
                  .DependsOn(Dependency.OnComponent("dependency","SomeDependency")));
      
    2. 如果特定组件可能需要多个 IDependency 才能使用,因此我将更改 MyComponent 以依赖 IDependencyFactory 本身和 IDependency 以声明它回答哪个 int 参数。大致如下:

      public interface IDependency
      {
          int Argument { get; set; }
      }
      public abstract class DependencyBase : IDependency
      {
          public DependencyBase(int argument) { Argument = argument; }
          public int Argument { get; set; }
      }
      
      public class DependencyA : DependencyBase { public DependencyA() : base(1) {} }
      public class DependencyB : DependencyBase { public DependencyB() : base(2) {} }
      public interface IDependencyFactory
      {
          IDependency Create(int argument);
      }
      
      public interface IComponent { }
      public class MyComponent : IComponent
      {
          public MyComponent(IDependencyFactory dependencyFactory)
          {
              DependencyFactory = dependencyFactory;
          }
      
          public IDependencyFactory DependencyFactory { get; set; }
      }
      public class Installer : IWindsorInstaller
      {
          public void Install(IWindsorContainer container, IConfigurationStore store)
          {
              container.Register(
                  Component.For<IDependency>().ImplementedBy<DependencyA>(),
                  Component.For<IDependency>().ImplementedBy<DependencyB>(),
                  Component.For<IDependencyFactory>().AsFactory(),
                  Component.For<IComponent>().ImplementedBy<MyComponent>());
          }
      }
      

      并且还实现了一个 ITypedFactoryComoponentSelector,它将通过参数 intager 从容器中解析。

    基本上,如果您已经使用 AsFactory,我会说“为什么我需要实现工厂”(或 ITypedFactoryComoponentSelector)是正确的 - 但 TypedFactoryFacility 是开箱即用的映射形式在不同的对象类型之间返回不同的对象而不是不同的对象实例。也许阅读更多关于它的城堡文档。

    希望对你有帮助


    我已将 IComponent 和 IDependency 类更改为具有 DoSomething() 函数,以便我可以实际执行某些操作。

        public interface IDependency
    {
        void DoSomething();
        int Argument { get; set; }
    }
    public abstract class DependencyBase : IDependency
    {
        public DependencyBase(int argument) { Argument = argument; }
        public void DoSomething() { }
        public int Argument { get; set; }
    }
    

    现在 MyComponent 实现如下所示:

        public class MyComponent : IComponent
    {
        public MyComponent(IDependencyFactory dependencyFactory)
        {
            DependencyFactory = dependencyFactory;
        }
    
        public void DoSomething(int argument)
        {
            var selectedDependency = DependencyFactory.Create(argument);
            selectedDependency.DoSomething();
        }
    
        public IDependencyFactory DependencyFactory { get; set; }
    }
    

    我已经实现了一个 ITypedFactoryComponentSelector:

        public class DependencyComponentSelector : ITypedFactoryComponentSelector
    {
        public DependencyComponentSelector(IKernel kernel)
        {
            Kernel = kernel;
        }
        public Func<IKernelInternal, IReleasePolicy, object> SelectComponent(MethodInfo method, Type type, object[] arguments)
        {
            return (k, r) =>
            {
                return from dependency in k.ResolveAll<IDependency>()
                       where dependency.Argument == int.Parse(arguments[0].ToString())
                       select dependency;
            };
        }
        public IKernel Kernel { get; set; }
    }
    

    (显然应该添加输入验证以检查它是否已为正确的工厂方法调用)

    然后在安装程序里把工厂的注册改成:

    Component.For<DependencyComponentSelector>(),
    Component.For<IDependencyFactory>().AsFactory(c=> c.SelectedWith<DependencyComponentSelector>()),
    

    【讨论】:

    • 嗯,问题是MyComponent 确实需要IDependency 的许多实现之一(如您的第二个示例)。我宁愿不让MyComponent 控制它所获得的IDependency 的实现,而是在其他地方处理。
    • 是的——你说“我不想让MyComponent 对哪个实现有任何控制权”是完全正确的。这就是为什么拥有工厂是件好事。现在,也不是工厂决定哪一个适合该组件,而是他决定哪个适合int。请参阅上面的答案 - 我添加了一些额外的代码。
    • 我稍微编辑了我的问题,希望能更清楚地说明问题 - 我希望在其他地方选择和创建 IDependency,主要是因为这个问题发生在依赖关系解决的每个级别.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多