Home > Enterprise >  Is there a type safe way to access command line arguments using the HostBuilder?
Is there a type safe way to access command line arguments using the HostBuilder?

Time:10-21

I am currently writing a console application and want to use HostBuilder for the sake of simple logging depedency injection without adding a larger console SDK.

I ran into this article which showed me how to access a command line argument - It works fine but i am wondering if i am missing some documentation, giving me a way to access these arguments using a IOptions<CliArguments> injection or something similar. Why IOptions? CLI arguments may not be set, so having an option to set default values would be easy through option configuration.

Is there documentation i am missing, or is there just no built in way to do this?

CodePudding user response:

Commandline args are documented as follows, which is a bit misleading when you want to use IOptions etc.

dotnet run key1=value1 --key2=value2 /key3=value3 --key4 value4 /key5 value5

You will get the behaviour you get from other configuration providers, which I think is what you're after by including a section name in the args e.g.

--MySectionName:MyKey=value

Then depending on how you're doing things in code, something like:

services.AddOptions<MyConfig>()                    
   .Bind(hostingContext.Configuration.GetSection("MySection"));

Given you have some config class:

public class MyConfig
{
    public string MyKey { get; set; }
}

Probably other approaches but it is understanding how providers and sections work. You could make args experience easier by prefixing the section in code, or look at binding single values.

CodePudding user response:

extension code

public static class CliArgumentBinderExtensions
{
    private class CliArgumentBinder<TOption> : IConfigureOptions<TOption> where TOption : class
    {
        private readonly IConfiguration _configuration;

        public CliArgumentBinder(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public void Configure(TOption options)
        {
            var properties = typeof(TOption).GetProperties();
            foreach (var propertyInfo in properties)
            {
                if(!propertyInfo.CanRead || !propertyInfo.CanWrite)
                    continue;

                propertyInfo.SetValue(options, _configuration[propertyInfo.Name]);
            }
        }
    }

    public static void AddCliArguments<TArgumentClass>(this IServiceCollection source, IConfiguration configuration) 
        where TArgumentClass : class, new()
    {
        source.AddTransient<IConfigureOptions<TArgumentClass>, CliArgumentBinder<TArgumentClass>>();
        source.Configure<TArgumentClass>(configuration);
    }
}


public class CliArguments
{
    public string RootPath { get; set; }
}

Using that extension it is possible to inject IOptions<CliArguments> into a consumer class which will automatically set properties based on the values retrieved through configuration.

console code

var builder = new HostBuilder();
builder
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        config.AddJsonFile("appsettings.json", optional: true);
        config.AddEnvironmentVariables();

        if (args != null)
        {
            config.AddCommandLine(args);
        }
    })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddOptions();
        services.Configure<ApplicationSettings>(hostContext.Configuration.GetSection("ApplicationSettings"));
        services.AddCliArguments<CliArguments>(hostContext.Configuration);

    })
    .ConfigureLogging((hostingContext, logging) =>
    {
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
        logging.AddConsole();
    });

await builder.RunConsoleAsync();
  • Related