如何配置windsor以通过依赖关系树作为参数传递依赖关系

本文关键字:关系 依赖 参数传递 何配置 配置 windsor | 更新日期: 2023-09-27 18:17:53

我有以下组件组成:

public interface IJob {
    ILogger Logger { get; set; }
}
public class JobC : IJob
{
    public ILogger Logger { get; set; }
    private ServiceA serviceA;
    private ServiceB serviceB;
    public JobC(ServiceA serviceA, ServiceB serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }
}
public class ServiceB
{
    public ILogger Logger { get; set; }
}
public class ServiceA
{
    public ILogger Logger { get; set; }
}

如你所见,到处都是Logger属性。问题是,我需要在解析期间传递该属性值(不同的作业需要不同的配置日志记录器)。因此,如果只有顶部组件需要这个,它将像

一样简单
var childLogger = Logger.CreateChildLogger(jobGroupName);
var job = windsorContainer.Resolve(jobType);
job.Logger = childLogger;

但是我需要传递childLogger到树和树是相当复杂的,我不希望手动传递logger实例到每个组件,需要它,想知道如果温莎可以帮助我在这一点?

更新:可能这将有助于更好地理解问题:在wiki中有一个通知:

内联依赖不会被传播无论你传递给Resolve方法的参数是什么,都只能对根组件有效
你要解决的问题,以及它的拦截器。所有根下的分量依赖项,以及它们的依赖项等等)将无法访问它们。

为什么会这样,有什么解决办法吗?

更新2:如果我加上一些真实的情况,也许会有所帮助。

所以,我们有应用程序,它发送/接收来自/到各种销售渠道的数据。每个销售渠道都有相应的作业集合,例如发送更新的产品信息、接收订单等(每个作业内部可能包含较小的作业)。因此,我们需要将每个通道的日志信息与其他通道的日志信息分开,这是合乎逻辑的,但是单个通道的作业日志应该进入单个侦听器,这样我们就可以看到正在发生的事情的顺序(如果每个作业和子作业都有自己的日志侦听器,我们就需要按时间合并日志,以了解发生了什么)。有些通道及其作业集在编译时是未知的(假设有通道A,我们可以通过简单地将该国家添加到DB来启动特定国家的单独通道,根据负载我们可以切换同步方法等)。

这意味着,我们可能有UpdateProductsForChannelAJob,它将在两个不同的通道(ChannelA US和ChannelA UK)中使用,所以它的记录器将取决于它依赖于哪个通道。

所以我们现在正在做的是,我们为每个通道创建子logger,并在解析Job实例作为参数时传递它。这是可行的,但有一个令人讨厌的事情-我们必须在作业中手动传递logger实例给每个依赖项(以及依赖项的依赖项),这可能是记录一些东西。

更新3:

我在Windsor文档功能中发现,这听起来像是我需要的:

有时需要提供依赖项,直到组件创建时才知道。例如,假设您需要服务的创建时间戳。您知道如何在注册时获得它,但不知道它的具体值是多少(实际上,每次创建新实例时它都会有所不同)。在这个场景中,您使用DynamicParameters方法。

在DynamicParameters委托中有两个参数,一个是dictionary和

就是这个字典,你现在可以填充依赖项,这些依赖项将进一步传递给解析管道

考虑到这一点,我认为这将工作:

public interface IService
{
}
public class ServiceWithLogger : IService
{
    public ILogger Logger { get; set; }
}
public class ServiceComposition
{
    public ILogger Logger { get; set; }
    public IService Service { get; set; }
    public ServiceComposition(IService service)
    {
        Service = service;
    }
} 
public class NameService
{
    public NameService(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}
public class NameServiceConsumer
{       
    public NameService NameService { get; set; }
}
public class NameServiceConsumerComposition
{       
    public NameService NameService { get; set; }
    public NameServiceConsumer NameServiceConsumer { get; set; }
}
[TestFixture]
public class Tests
{
    [Test]
    public void GivenDynamicParamtersConfigurationContainerShouldPassLoggerDownTheTree()
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>();
        container.Register(
            Component.For<IService>().ImplementedBy<ServiceWithLogger>().LifestyleTransient(),
            Component.For<ServiceComposition>().DynamicParameters((k, d) =>
            {
                d["Logger"] = k.Resolve<ILogger>().CreateChildLogger(d["name"].ToString());
            }).LifestyleTransient()
            );
        var service = container.Resolve<ServiceComposition>(new { name = "my child" });
        var childLogger = ((ServiceWithLogger) service.Service).Logger;
        Assert.IsTrue(((ConsoleLogger)childLogger).Name.Contains("my child"));
    }
    [Test]
    public void GivenDynamicParamtersConfigurationContainerShouldPassNameDownTheTree()
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>();
        container.Register(
            Component.For<NameService>().LifestyleTransient().DependsOn(new {name = "default"}),
            Component.For<NameServiceConsumer>().LifestyleTransient(),
            Component.For<NameServiceConsumerComposition>().DynamicParameters((k, d) =>
            {
                d["nameService"] = k.Resolve<NameService>(d["nameParam"]);
            }).LifestyleTransient()
            );
        var service = container.Resolve<NameServiceConsumerComposition>(new { nameParam = "my child" });
        Console.WriteLine(service.NameServiceConsumer.NameService.Name);
        Assert.IsTrue(service.NameServiceConsumer.NameService.Name.Contains("my child"));
    }
}

如何配置windsor以通过依赖关系树作为参数传递依赖关系

在解析后不要手动传递日志记录器。让温莎来帮你。使用日志工具

我想我终于明白了为什么这相对容易实现,但在Windsor中没有实现(我认为任何其他容器)。假设我们有以下配置:

public class TransientA
{
    public SingletonC SingletonC { get; set; }
    public ILogger Logger { get; set; }
}
public class TransientB
{
    public SingletonC SingletonC { get; set; }
    public ILogger Logger { get; set; }
}
public class SingletonC
{
    public ILogger Logger { get; set; }
}

类名反映了它们的生活方式,所以如果你要在Resolve for TransientA上做递归属性注入,你也要改变TransientB.SingletonC.Logger属性!

您可以在传播时跳过singleton属性注入,但是a)会增加混淆b)无论如何都不会解决初始问题(一些日志记录将进入singleton的日志记录器)。

所以在使用递归属性注入的情况下,你需要添加限制,该组件不应该有单例依赖(PerWebRequest/PerThread也)在它的依赖层次,这是相当有限的。