Why Startup's IConfigurationRoot is null?

2019-09-05 06:22发布

问题:

I'm trying to pass command line arguments to Startup class. Following this example, I modified my Program class and it looks like this:

        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("generalsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables(prefix: "ASPNETCORE_")
            .AddCommandLine(args);  
        var config = builder.Build();

        var host = new WebHostBuilder()
            .UseUrls("http://*:5000")
            .UseConfiguration(config)
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();

generalsettings.json contains following data:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Therefore I commented out default Startup class' constructor. Then I noticed that that's where IConfigurationRoot Configuration is assigned, and therefore, when I'm trying to use it from ConfigureServices it's null. I'm trying to use (I think) configuration built in Program's Main. What am I missing here?

UPDATE

To make it clear: I'm trying to use args in Startup class.

回答1:

It turns out that there has been a fair amount of discussion about this sort of thing in GitHub Aspnet hosting repo issues area. The GitHub repositories are always a good place to look on an interesting problem like this one.

To sum things up for you, do not set up IConfigurationRoot in Program.Main, set it up in Startup. You can pass the command line arguments to the Startup constructor from Program.cs like so:

    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .ConfigureServices(services => services
                .AddSingleton(new ConsoleArgs(args))
            )
            .Build();
        host.Run();
    }

where ConsoleArgs is a holder class you can create yourself that looks like:

public class ConsoleArgs
{
    public ConsoleArgs(string[] args)
    {
        Args = args;
    }
    public string[] Args { get; }
}

Obviously, configuring a ConsoleArgs service is the key. This will allow it to be injected into the Startup constructor. Then your Startup class constructor looks something like

public Startup(IHostingEnvironment env, ConsoleArgs args)
{
     var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile("generalsettings.json", optional: false, reloadOnChange: true)
    .AddEnvironmentVariables(prefix: "ASPNETCORE_")
    .AddCommandLine(args.Args);  
    var config = builder.Build();
}

The pattern here is "configure you hosting environment Program, configure you application in Startup"