简体   繁体   中英

.Net Core DI pass runtime parameter into constructor of service and also into 1-n of its' subservices

I have multiple let's say process services. These services have a property which I need to fill before using particular service. However these process services use also 1-n generator sub-services which also have same property as process service.

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;
    }
}

I use DI and resolve dependencies in 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();
    }
}

And use ProcessFactory class and its' method GetProcess for retrieving processes I want to use. I add these processes in List<IProcess> and then use this list for executing particular method from all retrieved IProcess services. configData are provided by user input and it also include our wanted property 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);
    }
}

For now I added Metadata to IProcess service after retrieving it by GetProcess method while Metadata has public setter. But it doesn't solve problem of how to pass it to generator sub-services. Don't want to go through all generator instances of process service and add Metadata through public setter. Is there any way of achieving this?

One way to do that is creating a scoped service to provide access to the metadata:

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

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

Register the service as scoped:

serviceCollection.AddScoped<IMetadataAccessor, MetadataProcessor>();

Change your process and generator classes to have IMetadataAccessor injected via constructor, and read metadata like this:

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

Change your factory to instantiate process within a scope:

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM