温莎城堡抽象工厂

时间:2018-01-18 16:21:24

标签: c# dependency-injection castle-windsor

我试图了解如何使用TypedFactoryFacility创建一个抽象工厂,我让它在基础层面上工作,但是我不完全理解如何使用运行时来扩展它依赖

假设我有一个需要在运行时创建的服务:

public interface IRuntimeService {
  void DoThing();
}

具有以下实现

public class RuntimeService : IRuntimeService {
  public void DoThing() {
    // Do some work
  }
}

要创建我的IRuntimeService,我已经创建了一个抽象工厂

public interface IRuntimeServiceFactory {
  IRuntimeService CreateService();
}

在我的Castle安装程序中,我使用TypedFactoryFacility注册我的班级和抽象工厂。

public class TypeInstaller : IWindsorInstaller {

  public void Install(IWindsorContainer container, IConfigurationStore store) {
    container.AddFacility<TypedFactoryFacility>();
    container.Register(Component.For<IRuntimeService>().ImplementedBy<RuntimeService>());
    container.Register(Component.For<IRuntimeServiceFactory>().AsFactory());
  }

然后在我将要使用该服务的类中,我可以使用工厂在运行时创建新的服务实例。

var myService = m_ServiceFactory.CreateService();

以上所有内容都非常有效,但是当我的RuntimeService类需要注入一个包含运行时参数的依赖链本身时,我遇到了问题。

要扩展上面的示例,假设我有一个新的运行时依赖

public interface IRuntimeDependency {
  void DoWork();
}

由一个通过构造函数

获取运行时字符串值的类实现
public class RuntimeDependency : IRuntimeDependency {

  private readonly string m_Param;

  public RuntimeDependency(string param) {
    m_Param = param;
  }

  public void DoWork() {
    // Do work involving the param
  }
}

以前定义的服务类现在需要对依赖项的引用

public class RuntimeService : IRuntimeService {

  private readonly IRuntimeDependency m_Dep;

  public RuntimeService(IRuntimeDependency dep) {
    m_Dep = dep;
  }

  public void DoThing() {
    // Do some work involving the dependency
    m_Dep.DoWork();
  }
}

我现在如何使用TypedFactoryFacility创建服务实例?

我希望能够将我的工厂方法更改为

IRuntimeService CreateService(string param);

但是Windsor抛出错误&#39;无法解析参数&#39; param&#39;的非可选依赖项。输入&#39; System.String&#39;。

如果我给它一个字符串,Windsor知道如何创建IRuntimeDependency,如果我给它依赖,它知道如何创建IRuntimeService,那么为什么不能直接创建它带有字符串参数的IRuntimeService

我可以通过两种不同的工厂方法来实现它的工作

IRuntimeService CreateService(IRuntimeDependency dep);
IRuntimeDependency CreateDependency(string param);

并手动创建依赖项

var dep = m_ServiceFactory.CreateDependency(param);
var myService = m_ServiceFactory.CreateService(dep );

^^^这个工作,但是使用容器的重点是它将负责为我组装新对象。这是一个只涉及一个依赖关系的相对简单的例子,但它很容易失控,使用更复杂的对象图。

我当然可以创建自己的工厂实现,但这也会使使用TypedFactoryFacility的好处无效,而FactoryComponentSelector应该为您创建抽象工厂实现。我很难相信这个问题不是现有解决方案,但Windsor示例不包含任何链式运行时依赖项。

我不认为使用RuntimeService是正确的方法,因为只有一条可能的路径来创建<label id="internet_ipaddr" class="label_s1"></label> 实例。它应该能够自动解决。

3 个答案:

答案 0 :(得分:2)

在许多或大多数情况下,容器解析的对象取决于容器也解析的其他接口的实现。因此,只要所有接口都已注册实现,容器就可以解析整个依赖关系链。

但在这种情况下RuntimeDependency取决于字符串,而不是容器可以解析的字符串。

public RuntimeDependency(string param) {
    m_Param = param;
}

在这种情况下,您可以使用DependsOn方法显式提供值来实现该依赖关系。

container.Register(Component.For<IRuntimeDependency, RuntimeDependency>()
    .DependsOn(Dependency.OnValue("param","whatEverTheValueIs")));

当然,这个价值可以来自配置或其他任何地方。我在SQL连接字符串中使用了很多。

答案 1 :(得分:1)

可以使用DynamicParameters

container.Register(Component.For<IRuntimeService>()
    .ImplementedBy<RuntimeService>()
    .LifestyleTransient()
    .DynamicParameters((k, d) => {
        d["dep"] = new RuntimeDependency((string)d["param"]);
    }));

请记住,字典键必须与CreateService方法和RuntimeService构造函数中的参数名称匹配。

编辑:如果您打算在每次调用工厂方法时创建新实例,也应该将其设为LifestyleTransient。 (默认为单身)

答案 2 :(得分:0)

似乎我要求的是设计不可能。

见另一个SO答案。

https://stackoverflow.com/a/3905496/2029835