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

发布于 2024-09-27 02:23:45 字数 844 浏览 1 评论 0原文

以下代码仅用于演示目的。

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

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

从 UI 类中,我需要解析业务服务,因此我编写了以下代码:

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

注意依赖项是 connectionString 构造函数参数。

但是上面的代码不起作用,说 dataService 期望依赖项不满意。

无法创建组件“dataService” 因为它有依赖关系 使满意。数据服务正在等待 以下依赖项:

按键(具有特定按键的组件) - 未注册的依赖关系。

因此,作为一种解决方法,我正在这样做:

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

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

因此,如果您能告诉我们为什么它不能以简单的方式工作,或者您有更好的解决方法,请分享。

The following code is for demo purposes only.

Lets say i have 2 components (businessService, and dataService), and a UI class.

UI class needs a business service, businessService needs a dataService, and dataService relies on a connectionString.

Form the UI class i need to resolve the business service, so i am writing the below code:

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

notice that dependancy is the connectionString constructor parameter.

But the above code is not working, saying that dataService expecting dependancy which was not satisified.

Can't create component 'dataService'
as it has dependencies to be
satisfied. dataService is waiting for
the following dependencies:

Keys (components with specific keys)
- dependancy which was not registered.

So as a workaround i am doing this:

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

But from design, coding style and many perspectives this is not a good way of doing it.

So please if you can advise why its not working in the simple way or you have a better workaround please share.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

许你一世情深 2024-10-04 02:23:45

您看到的行为是设计使然的。

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

文档详细说明了它,所以我不会在这里重申这一点。

更新

为了清楚起见 - Windsor 不会在解析管道中传递内联参数。原因很简单——这样做会破坏抽象。调用代码必须隐式知道您的 BusinessService 依赖于 DataService,而 DataService 又依赖于连接字符串。

如果您绝对必须这样做,请明确说明。这几乎就是您正在做的事情 - 显式解析 DataService 及其对连接字符串的依赖关系,并显式解析传递 DataServiceBusinessService作为依赖。

为了使事情真正明确(并且也更好用),我建议使用 Typed Factory 来代替直接调用容器

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

The behavior you see is by design.

There are a couple of ways to approach the problem, depending on how dynamic the value you want to pass down is.

The documentation does a pretty good job detailing it, so I won't reiterate that here.

Update

To clarity - Windsor does not pass inline arguments down the resolution pipeline. The reason for that is simple - doing so would break an abstraction. Calling code would have to implicitly know that your BusinessService depends on DataService which depends on connection string.

If you absolutely have to do it, than do make this explicit. That is do pretty much what you're doing - resolve the DataService with its dependency on connection string explicitly, and explicitly resolve BusinessService passing the DataService as dependency.

To make things really explicit (and nicer to use as well) I'd suggest using Typed Factory for that instead of directly calling the container

public interface IFactory
{
   IDataService ResolveDataService(string connectionString);
   IBussinessService ResolveBussinessService(IDataService dataService);
   // possibly release method for IBussinessService as well
}
零度℉ 2024-10-04 02:23:45

在创建需要上下文对象的瞬态组件时,我需要这样做。我使用的解决方案是重写 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());

I have needed to do this when creating transient components that require a context object. The solution I used was to override the DefaultDependencyResolver class so that it does pass inline arguments down the resolution pipeline.

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);
    }
}

An instance of this class needs to be passed in when the container is created (other classes also need to be passed in because there's no convenient constructor that only takes a dependency resolver):

var container = new WindsorContainer(
    new DefaultKernel(
        new ArgumentPassingDependencyResolver(),
        new NotSupportedProxyFactory()),
    new DefaultComponentInstaller());
柒夜笙歌凉 2024-10-04 02:23:45

是的,您所要求的是可能的,但您应该通过 类型化工厂设施,而不是直接通过容器请求服务。

对于类型化工厂,您所需要做的就是定义工厂接口,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 注册中。

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

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

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

Yes, what you're requesting is possible, but you should be using abstract factories through the Typed Factory Facility instead of requesting your service directly through the container.

With typed factories, all you need to do is define the factory interfaces and Windsor will take care of the implementation for you.

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

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

You add the facility and register your factories interface as follows:

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

Your can now manually define how your runtime parameter gets passed down the object graph by defining a Dynamic Parameter in your BusinessServiceregistration.

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

Keep in mind that the dictionary keys have to match the parameter names in the CreateBusinessService method and BusinessService constructor.

You should also make it LifestyleTransient if you intend to create a new instance each time the factory method is called. (The default is singleton)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文