【问题标题】:.NET core Pass Commandline Args to Startup.cs from Program.cs.NET 核心将命令行参数从 Program.cs 传递给 Startup.cs
【发布时间】:2018-11-29 22:35:42
【问题描述】:

我正在尝试配置 kestrel,以便当它处于原始模式时,它可以在特定端口上运行。但是,要这样做,launchsettings.json 似乎需要传递命令行参数才能这样做,因为没有直接选项,并且它总是在端口 5000 上运行,如果你有一个需要运行的 api 和一个网站,这显然会发生冲突.

所以我将 CommandLine 包添加到我的站点,您确实可以在 startup.cs 文件中使用 builder.AddCommandLine()。

问题是如何将 args 从 program.cs 获取到 Startup.cs 或查找它们而不是静态变量。

如果您无法获取参数,这会使扩展方法变得毫无意义。

有更好的方法吗?

【问题讨论】:

  • 您是否正在尝试使用命令行上指定的端口运行您的应用程序,例如dotnet run myproject --port 3333
  • 是或者让 vs.Net 或 vs 代码指定一个唯一的端口,这样两个站点可以同时运行。

标签: c# .net-core


【解决方案1】:

一个简单的解决方案是通过Environment.GetCommandLineArgs 方法访问命令行参数。

您只需要确保删除第一个参数,即可执行文件名称:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var args = Environment.GetCommandLineArgs().Skip(1).ToArray();
        var builder = new ConfigurationBuilder();
        builder.AddCommandLine(args);

        Configuration = builder.Build();
    }
}

【讨论】:

  • 这工作完美,是一个简单的解决方案。
【解决方案2】:

更新

我实际上找到了看起来更优雅的解决方案:

  1. 在 Program 中将命令行参数解析为 IConfigurationRoot(使用 CommandLineApplication、好文章和示例 here
  2. 只需通过 DI 容器将此 IConfigurationRoot 传递给 Startup

像这样:

public static IWebHost BuildWebHost(string[] args)
{
    var configuration = LoadConfiguration(args);

    // Use Startup as always, register IConfigurationRoot to services
    return new WebHostBuilder()
        .UseKestrel()
        .UseConfiguration(configuration)
        .ConfigureServices(s => s.AddSingleton<IConfigurationRoot>(configuration))
        .UseStartup<Startup>()
        .Build();
}

public class Startup
{
    public Startup(IConfigurationRoot configuration)
    {
        // You get configuration in Startup constructor or wherever you need
    }
}

LoadConfiguration 的示例实现,它解析 args 并构建 IConfigurationRoot(在此示例中,配置文件名可以在命令行参数中覆盖):

private static IConfigurationRoot LoadConfiguration(string[] args)
{
    var configurationFileName = "configuration.json";

    var cla = new CommandLineApplication(throwOnUnexpectedArg: true);

    var configFileOption = cla.Option("--config <configuration_filename>", "File name of configuration", CommandOptionType.SingleValue);

    cla.OnExecute(() =>
    {
        if (configFileOption.HasValue())
            configurationFileName = configFileOption.Value();

        return 0;
    });

    cla.Execute(args);

    return new ConfigurationBuilder()
        .SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
        .AddJsonFile(configurationFileName, optional: false, reloadOnChange: true)
        .AddCommandLine(args)
        .Build();
}

老答案

您可以自己实例化 Startup 类并将其作为实例传递给 WebHostBuilder。它有点不那么优雅,但可行。来自here.

public static IWebHost BuildWebHost(string[] args)
{
    // Load configuration and append command line args
    var config = new ConfigurationBuilder()
        .SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
        .AddJsonFile("configuration.json")
        .AddCommandLine(args)
        .Build();

    // pass config to Startup instance
    var startup = new Startup(config);

    // Instead of using UseStartup<Startup>()
    // Register startup to services
    return new WebHostBuilder()
        .UseKestrel()
        .UseSetting("applicationName", "Your.Assembly.Name")
        .UseConfiguration(config)
        .ConfigureServices(services => services.AddSingleton<IStartup>(startup))
        .Build();
}

几个注意事项是:

  • 通过这样做,Startup 应该实现 IStartup,这对于 Configure 方法的参数仅限于 Configure(IApplicationBuilder app) 而不是完整的 Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime lifetime)
  • 出于某种原因,您需要像我的示例中那样手动指定applicationName 参数。我正在2.0.0-preview1-final 上测试这个

【讨论】:

  • 不错,但没关系。这些东西在 vs.net 2017 中应该很容易实现并且很容易设置。但是不,这是一个 hack。
【解决方案3】:

Kestrel 可以配置为以多种方式侦听不同的端口。这些方法都不需要在Startup 类中发生,而是在Program 类的Main 方法中发生。使用AddCommandLine 扩展方法就是其中之一。要使用它,请将 Program.cs 文件的 Main 方法修改为如下所示:

public static void Main(string[] args)
{
    var config = new ConfigurationBuilder()
        .AddCommandLine(args)
        .Build();

    var host = new WebHostBuilder()
                .UseKestrel()
                .UseConfiguration(config)
                .UseStartup<Startup>()
                .Build();
    host.Run();
}

然后,使用dotnet run --server.urls http://*:&lt;yourport&gt; 运行应用程序,将&lt;yourport&gt; 替换为您希望它运行的实际端口号。 * 使其监听所有可用的 IP 地址,如果您想监听特定地址,则需要在此处指定它而不是 *

更改端口的另一个选项是使用.UseUrls 方法对端口和地址进行硬编码。例如:

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
                .UseKestrel()
                .UseUrls("http://*:8080")
                .UseStartup<Startup>()
                .Build();
    host.Run();
}

此示例将使您的应用程序侦听所有可用 IP 地址上的端口 8080

【讨论】:

  • 我的问题是现在启动与 startup.cs 分离,并且在正确的意义上注入测试内容不再可能,因为配置创建发生在主 ND 而不是启动中。也放入使用 URL 似乎很好,但我希望这不会与 azure deploy 混淆?
  • 如果您使用 AddCommandLine 选项,它不应该干扰 azure deploy,因为它只会更改在命令行中指定的端口。
  • 好的,但这仍然将配置创建与 startup.cs 和 di 解耦。
  • @DanielGrim 他需要将命令行参数传递给 Startup,您的解决方案没有解决这个问题。
猜你喜欢
  • 1970-01-01
  • 2012-11-23
  • 2015-06-09
  • 2017-10-22
  • 2019-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多