Как настроить windsor для передачи зависимости в качестве аргумента через дерево зависимостей?

У меня есть следующий состав компонентов:

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 вниз по дереву, а это дерево довольно сложное, я не хочу вручную передавать экземпляр логгера каждому компоненту, которому он нужен, интересно, может ли Виндзор помочь мне в этом?

Обновление: Возможно, это поможет лучше понять проблему: В wiki есть замечание:

Inline dependencies don't get propagated Все аргументы, которые вы передаете методу Resolve, будут доступны только для корневого компонента
, который вы пытаетесь разрешить, и его Interceptors. Все компоненты, расположенные ниже (корневой компонент зависимости, их зависимости и так далее) не будут иметь к ним доступа.

Почему так происходит и есть ли обходной путь?

Update 2: Возможно, это поможет, если я добавлю реальную ситуацию.

Итак, у нас есть приложение, которое отправляет/получает данные от/к различным каналам продаж. Каждый канал продаж имеет соответствующую коллекцию заданий, таких как отправка обновленной информации о продукте, получение заказов и т.д. (каждое задание может содержать внутри себя более мелкие задания). Поэтому логично, что нам нужно хранить информацию журнала каждого канала отдельно от информации других каналов, но журналы заданий одного канала должны идти к одному слушателю, чтобы мы могли видеть последовательность происходящего (если бы у каждого задания и подзадания был свой слушатель, нам пришлось бы объединять журналы по времени, чтобы понять, что происходит). Некоторые каналы и их наборы заданий не известны во время компиляции (допустим, есть канал A, мы можем запустить отдельный канал для конкретной страны, просто добавив эту страну в DB, в зависимости от нагрузки мы можем переключить метод синхронизации и т.д.).

Все это означает, что у нас может быть UpdateProductsForChannelAJob, который будет использоваться в двух разных каналах (ChannelA US и ChannelA UK), поэтому его логгер будет зависеть от того, от какого канала он зависит.

Поэтому сейчас мы создаем дочерний логгер для каждого канала и передаем его в качестве параметра при разрешении экземпляра Job. Это работает, но есть один неприятный момент - нам приходится передавать экземпляр логгера вручную внутри job каждой зависимости (и зависимости зависимостей), которая может что-то логировать.

Обновление 3:

Я нашел в документации Виндовс функцию, которая звучит как то, что мне нужно:

Бывают случаи, когда вам нужно предоставить зависимость, которая не будет известна до момента создания компонента. Например, скажем, вам нужна метка времени создания для вашего сервиса. Вы знаете, как получить ее во время регистрации, но не знаете, каким будет ее конкретное значение (и, более того, оно будет разным при каждом создании нового экземпляра). В этом сценарии вы используете метод 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();
        container.Register(
            Component.For().ImplementedBy().LifestyleTransient(),
            Component.For().DynamicParameters((k, d) =>
            {
                d["Logger"] = k.Resolve().CreateChildLogger(d["name"].ToString());
            }).LifestyleTransient()
            );

        var service = container.Resolve(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();
        container.Register(
            Component.For().LifestyleTransient().DependsOn(new {name = "default"}),
            Component.For().LifestyleTransient(),
            Component.For().DynamicParameters((k, d) =>
            {
                d["nameService"] = k.Resolve(d["nameParam"]);
            }).LifestyleTransient()
            );

        var service = container.Resolve(new { nameParam = "my child" });
        Console.WriteLine(service.NameServiceConsumer.NameService.Name);
        Assert.IsTrue(service.NameServiceConsumer.NameService.Name.Contains("my child"));
    }
}

Но это не так.

5
задан Giedrius 9 May 2019 в 05:49
поделиться