Castle Windsor IOC:将构造函数参数传递给子组件

时间:2010-10-11 09:23:31

标签: .net castle-windsor

以下代码仅供演示之用。

假设我有2个组件(businessService和dataService)和一个UI类。

UI类需要业务服务,businessService需要dataService,而dataService需要依赖connectionString。

形成我需要解析业务服务的UI类,所以我写下面的代码:

var service = container.Resolve<BusinessService>(new { dependancy = "con string 123" }));

注意,dependance是connectionString构造函数参数。

但是上面的代码没有用,说dataService期望依赖性不满意。

  

无法创建组件'dataService'   因为它有依赖性   满意。 dataService正在等待   以下依赖项:

     

键(具有特定键的组件)    - 未登记的依赖性。

作为一种解决方法,我这样做:

var service = container.Resolve<BusinessService>(new { dataService = container.Resolve<IDataService>(new { dependancy = "123" }) });

但是从设计,编码风格和许多角度来看,这不是一个很好的方法。

所以,如果你能告诉它为什么不能以简单的方式工作,或者你有一个更好的解决方法,请分享。

3 个答案:

答案 0 :(得分:6)

您看到的行为是设计性的。

有两种方法可以解决问题,具体取决于您要传递的值的动态程度。

The documentation做得非常好,详细说明,所以我不会在此重申。

<强>更新

为了清晰起见 - Windsor不会在分辨率管道中传递内联参数。原因很简单 - 这样做会破坏抽象。调用代码必须隐含地知道您的BusinessService依赖于DataService,这取决于连接字符串。

如果你必须这样做,那就明白了。这就是你正在做的事情 - 明确解决DataService依赖于连接字符串的问题,并明确解决BusinessService传递DataService作为依赖关系。

为了使事情真正明确(并且更好用),我建议使用Typed Factory而不是直接调用容器

public interface IFactory
{
   IDataService ResolveDataService(string connectionString);
   IBussinessService ResolveBussinessService(IDataService dataService);
   // possibly release method for IBussinessService as well
}

答案 1 :(得分:2)

在创建需要上下文对象的瞬态组件时,我需要这样做。我使用的解决方案是覆盖DefaultDependencyResolver类,以便它确实在分辨率管道中传递内联参数。

public class ArgumentPassingDependencyResolver : DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            // this behaviour copied from base class
            return current;
        }

        // the difference in the following line is that "true" is passed
        // instead of "false" as the third parameter
        return new CreationContext(parameterType, current, true);
    }
}

创建容器时需要传入此类的实例(其他类也需要传入,因为没有方便的构造函数只接受依赖项解析器):

var container = new WindsorContainer(
    new DefaultKernel(
        new ArgumentPassingDependencyResolver(),
        new NotSupportedProxyFactory()),
    new DefaultComponentInstaller());

答案 2 :(得分:0)

是的,您要求的是可能的,但您应该通过Typed Factory Facility使用抽象工厂,而不是直接通过容器请求您的服务。

对于类型化工厂,您需要做的就是定义工厂接口,Windsor将为您完成实施。

public interface IBusinessServiceFactory {
  IBusinessService CreateBusinessService(string connString);
}

public interface IDataServiceFactory {
  IDataService CreateDataService(string connString);
}

添加工具并注册工厂界面,如下所示:

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IDataServiceFactory>().AsFactory());
container.Register(Component.For<IBusinessServiceFactory>().AsFactory());

您现在可以通过在BusinessService注册中定义Dynamic Parameter来手动定义运行时参数如何通过对象图传递。

container.Register(Component.For<IBusinessService, BusinessService>()
    .LifestyleTransient()
    .DynamicParameters((k, d) => {
        d["dataService"] = k.Resolve<IDataServiceFactory>.CreateDataService((string)d["connString"]);
    }));

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

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