如何配置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中没有实现(我认为任何其他容器)。假设我们有以下配置:
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也)在它的依赖层次,这是相当有限的。