我有以下组成部分:
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; }
}
Run Code Online (Sandbox Code Playgroud)
如您所见,到处都有Logger属性。问题是,我需要在解析期间传递该属性值(不同的作业需要不同的配置记录器)。因此,如果只有顶层组件需要它,那么它就像
var childLogger = Logger.CreateChildLogger(jobGroupName);
var job = windsorContainer.Resolve(jobType);
job.Logger = childLogger;
Run Code Online (Sandbox Code Playgroud)
但是我需要将childLogger传递到这棵树下,并且那棵树非常复杂,我不希望手动将logger实例传递给需要它的每个组件,想知道Windsor是否可以帮助我?
更新:也许这将有助于更好地理解问题:在Wiki中有以下通知:
内联依赖项不会传播。无论传递给Resolve方法的参数是什么,仅对您要解析的根组件
及其拦截器可用。向下的所有组件(root的依存关系及其依存关系等)将无法访问它们。
为什么会这样,并且有任何解决方法?
更新2:如果我添加实际情况,可能会有所帮助。
因此,我们有了Application,它可以从各种销售渠道发送/接收数据。每个销售渠道都有相应的作业集合,例如发送更新的产品信息,接收订单等(每个作业内部可能包含较小的作业)。因此,合乎逻辑的是,我们需要将每个通道的日志信息与其他通道的日志信息分开,但是单个通道的作业日志应该转到单个侦听器,这样我们就可以看到正在发生的顺序(如果每个作业和子作业都具有自己的日志记录侦听器,我们需要按时间合并日志以了解发生了什么)。有些通道及其工作集在编译时是未知的(假设有通道A,我们可以通过将特定国家/地区简单地添加到数据库中来启动针对特定国家/地区的单独通道,具体取决于我们可以切换同步方法等)。
这意味着什么,我们可能会有UpdateProductsForChannelAJob,它将在两个不同的渠道(ChannelA US和ChannelA UK)中使用,因此它的记录器将取决于它依赖于哪个渠道。
因此,我们现在要做的是为每个通道创建子记录器,并在将Job实例作为参数解析时传递它。那行得通,但有一件令人讨厌的事情-我们必须在作业内部手动将记录器实例传递给每个依赖项(和依赖项依赖项),这可能正在记录某些东西。
更新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"));
}
}
Run Code Online (Sandbox Code Playgroud)
但事实并非如此。
我想我终于明白了为什么这是相对容易实现的,但在 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; }
}
Run Code Online (Sandbox Code Playgroud)
类名反映了它们的生活方式,因此如果您要在 Resolve 上为 TransientA 进行递归属性注入,您也会更改 TransientB.SingletonC.Logger 属性!
您可以在传播时跳过单例属性注入,但是a)会增加混乱b)无论如何都不会解决初始问题(一些日志记录将转到单例的记录器)。
因此,如果要使用递归属性注入,您需要添加限制,该组件在其依赖层次结构中不应具有单例依赖项(也有 PerWebRequest/PerThread),这是相当有限的。