CommandLineParser.Core icon indicating copy to clipboard operation
CommandLineParser.Core copied to clipboard

DI: How to register Service, that needs parsed argument as parameter

Open SvenBM opened this issue 4 years ago • 3 comments

Hi, i want to build a cli wrapper around an existing API. The API is instantiated with a static factory method which has a parameter called environment (PROD or TEST). This environment should be an option for the cli. How do i register the service properly to use it in Commands with DI?

SvenBM avatar Nov 08 '21 15:11 SvenBM

Hi @SvenBM

I think I didn't understand the question correctly. Do you mean to register a service depending on the result of the CLI Option i.e. the PROD/TEST?

Maybe something like this is a solution?

Run

> wrapper.exe -e prod run
> Running API from PROD

Code

using MatthiWare.CommandLine;
using MatthiWare.CommandLine.Abstractions;
using MatthiWare.CommandLine.Abstractions.Command;
using MatthiWare.CommandLine.Core.Attributes;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Reflection;

namespace Question
{
    public enum ApiEnvironment
    {
        PROD,
        TEST
    }

    public class Options
    {
        [Name("e", "environment")]
        [Required]
        public ApiEnvironment Environment { get; set; }
    }

    public class Api
    {
        private readonly ApiEnvironment environment;

        private Api(ApiEnvironment environment)
        {
            this.environment = environment;
        }

        public static Api CreateApiFactory(ApiEnvironment environment)
            => new(environment);

        public void Run()
        {
            Console.WriteLine($"Running API from {environment}");
        }
    }

    public class RunCommand : Command<Options>
    {
        public override void OnConfigure(ICommandConfigurationBuilder builder)
        {
            builder.Name("run").Description("Runs the API");
        }

        public override void OnExecute(Options options)
        {
            var api = Api.CreateApiFactory(options.Environment);
            api.Run();
        }

        public class Program
        {
            static void Main(string[] args)
            {
                var services = ConfigureServices();

                var parser = services.BuildServiceProvider().GetRequiredService<ICommandLineParser<Options>>();

                parser.DiscoverCommands(Assembly.GetExecutingAssembly());

                parser.Parse(args);
            }

            static IServiceCollection ConfigureServices()
            {
                var services = new ServiceCollection();

                services.AddCommandLineParser<Options>();

                return services;
            }
        }
    }
}

Matthiee avatar Nov 08 '21 16:11 Matthiee

Hey, thx for your fast reply. Yes, you got it right, despite that my API has an interface. But when i code it like this, i have to instantiate the API in every command (there are more than 1). It would by nicer if i could register the API's interface to the services and then inject it into the command - the DI-container should be responsible for creating instances. Do you see some way? Or is it prohibited by the parse-executeCommand-chain?

SvenBM avatar Nov 08 '21 16:11 SvenBM

At the moment it is not possible to do this because the result of which API environment to use is only known after the service provider has already been built.

Matthiee avatar Nov 08 '21 17:11 Matthiee