У меня есть следующий состав компонентов:
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каждому компоненту, которому это нужно, интересно, может ли Виндзор помочь мне в этом?
Обновление: может быть, это поможет лучше понять проблему: в вики есть уведомление:
Встроенные зависимости не распространяются. Какие бы аргументы вы ни передавали методу Resolve, он будет доступен только корневому компоненту
, который вы пытаетесь разрешить, и его перехватчикам.Все компоненты, расположенные ниже (корневые зависимости, их зависимости и т. Д.) Не будут иметь к ним доступа.
Почему это так, и есть ли обходной путь?
Обновление2: Может быть, это поможет, если я добавлю реальную ситуацию.
Итак, у нас есть Приложение, которое отправляет / получает данные из / в различные каналы продаж.Каждый канал продаж имеет соответствующую коллекцию заданий, например, отправку обновленной информации о продукте, получение заказов и т. Д. (Каждое задание может содержать меньшие задания внутри).Поэтому логично, что нам нужно хранить информацию журнала каждого канала отдельно от журналов других каналов, но журналы заданий одного канала должны идти к одному слушателю, чтобы мы могли видеть последовательность происходящего (если бы у каждого задания и подзадачи был собственный слушатель регистрации)нам нужно было бы объединить журналы по времени, чтобы понять, что происходит).Некоторые каналы и их наборы заданий неизвестны во время компиляции (скажем, есть канал A, мы можем запустить отдельный канал для конкретной страны, просто добавив эту страну в БД, в зависимости от нагрузки, которую мы можем переключить методом синхронизации и т. Д.).
Что все это означает, что у нас может быть UpdateProductsForChannelAJob, который будет использоваться в двух разных каналах (ChannelA US и ChannelA UK), поэтому его регистратор будет зависеть от того, к какому каналу он относится.
Итак, что мы делаем сейчас, мы создаем дочерний логгер для каждого канала и передаем его при разрешении экземпляра Job в качестве параметра.Это работает, но есть одна неприятная вещь - мы должны вручную передавать экземпляр регистратора внутри задания каждой зависимости (и зависимости зависимостей), которая может что-то регистрировать.
Обновление 3:
I 'мы нашли в документации по Windsor функцию, которая звучит примерно так, как мне нужно:
В некоторых случаях вам нужно указать зависимость, которая не будет известна до момента создания компонента.Например, скажем, вам нужна временная метка создания для вашего сервиса.Вы знаете, как получить его во время регистрации, но не знаете, каким будет его конкретное значение (и действительно, оно будет отличаться при каждом создании нового экземпляра).В этих сценариях вы используете метод DynamicParameters.
И вы получаете два параметра в делегате DynamicParameters, один из которых - словарь и
Это тот словарь, который вы теперь можете заполнитьс зависимостями, которые будут переданы далее в конвейер разрешения
Учитывая это, я думал, что это будет работать:
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"));
}
}
Но это не так.