. Net Базовый DI передает параметр времени выполнения в конструктор службы, а также в 1-е его вспомогательных сервисов. - PullRequest
0 голосов
/ 03 апреля 2020

У меня есть несколько, скажем, сервисных процессов. У этих сервисов есть свойство, которое мне нужно заполнить перед использованием определенного сервиса. Однако эти сервисы процесса используют также под-сервисы генератора 1-n, которые также имеют то же свойство, что и сервис процесса.

public interface IProcess
{
    IEnumerable<string> metadata;
    //...
}

public class Process1 : IProcess
{
    public IEnumerable<string> Metadata {get; set;}
    private readonly IGenerator1 Generator1;
    private readonly IGenerator2 Generator2;

    public Process1(
        IGenerator1 generator1,
        IGenerator2 generator2,
        IEnumerable<string> metadata)
    {
        Generator1 = generator1;
        Generator2 = generator2;
        Metadata = metadata;
    }
}

public interface IGenerator
{
    IEnumerable<string> metadata;
    //...
}

public class Generator1 : IGenerator
{
    public IEnumerable<string> Metadata {get; set;}
    private readonly ILogger Logger;

    public Generator1(
        ILogger logger,
        IEnumerable<string> metadata)
    {
        Logger = logger;
        Metadata = metadata;
    }
}

Я использую DI и разрешаю зависимости в ServiceBuilder

public class ServiceBuilder
{
    public ServiceBuilder()
    {
        var services = new ServiceCollection();

        services.AddTransient<IProcess, Process1>();
        services.AddSingleton<IProcessFactory, ProcessFactory>();
        services.AddTransient<IDeliverySiteGenerator, DeliverySiteGenerator>();
        services.AddTransient<INewConnectionErrandGenerator, NewConnectionErrandGenerator>();
        //...
        ServiceProvider = services.BuildServiceProvider();
    }
}

И использую класс ProcessFactory и его метод GetProcess для получения процессов, которые я хочу использовать. Я добавляю эти процессы в List<IProcess>, а затем использую этот список для выполнения определенного метода из всех извлеченных сервисов IProcess. configData предоставляется пользовательским вводом, и он также включает в себя наше требуемое свойство IEnumerable<string> Metadata.

public class ProcessFactory : IProcessFactory
{
    private readonly IServiceProvider serviceProvider;
    //...
    public IProcess GetProcess(SomeConfigData configData)
    {
        var processExists = registeredProcesses.TryGetValue(configData, out var processType);

        if (!processExists)
            throw new InvalidArgumentException($"Process not supported.");

        return (IProcess)serviceProvider.GetService(processType);
    }
}

На данный момент я добавил Metadata в службу IProcess после извлечения его методом GetProcess, тогда как Metadata имеет установщик c. Но это не решает проблему того, как передать его вспомогательным сервисам генератора. Не хочу go через все экземпляры генератора службы процессов и добавлять Metadata через установщик publi c. Есть ли способ достичь этого?

1 Ответ

3 голосов
/ 03 апреля 2020

Один из способов сделать это - создать службу с областью действия для предоставления доступа к метаданным:

public interface IMetadataAccessor
{
    IEnumerable<string> Metadata { get; set; }
}

public class MetadataProcessor : IMetadataAccessor
{
    public IEnumerable<string> Metadata { get; set; }
}

Зарегистрировать службу как область действия:

serviceCollection.AddScoped<IMetadataAccessor, MetadataProcessor>();

Изменить класс процесса и генератора чтобы IMetadataAccessor вводили через конструктор и читали метаданные следующим образом:

public IEnumerable<string> Metadata => _metadataAccessor.Metadata;

Измените свою фабрику, чтобы создать экземпляр процесса в области:

public Processor CreateProcessor(IEnumerable<string> metadata)
{
    // Resolve services within a child scope
    using (var scope = _services.CreateScope())
    {
        // Resolve the accessor service and set metadata
        var accessor = scope.ServiceProvider.GetRequiredService<IMetadataAccessor>();
        accessor.Metadata = metadata;

        // Within the current scope, there is only one IMetadataAccessor.
        // So both process and generator will be getting same accessor instance via the constructor.
        // If we set metadata to the instance, all services can get the value.
        var process = scope.ServiceProvider.GetRequiredService<Process>();
        return process;
    }
}
...