如何使用IHostedService访问控制台应用程序中的命令行参数?



我不知道如何在我的ConsoleHostedService实现类中访问命令行参数。我在源代码中看到CreateDefaultBuilder(args)以某种方式将其添加到配置中…namedArgs

有主程序:

internal sealed class Program
{
private static async Task Main(string[] args)
{
await Host.CreateDefaultBuilder(args)
.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.ConfigureServices((context, services) =>
{
services.AddHostedService<ConsoleHostedService>();
})
.RunConsoleAsync();
}
}

和托管服务:

internal sealed class ConsoleHostedService : IHostedService
{
public ConsoleHostedService(
IHostApplicationLifetime appLifetime,
IServiceProvider serviceProvider)
{
//...
}
}

我不相信有一个内置的DI方法来获取命令行参数-但可能的原因是处理命令行参数是您的主机应用程序的责任,应该通过IConfigurationIOptions等传递主机/环境信息。

无论如何,只要定义你自己的注入:

public interface IEntrypointInfo
{
String CommandLine { get; }
IReadOnlyList<String> CommandLineArgs { get; }
// Default interface implementation, requires C# 8.0 or later:
Boolean HasFlag( String flagName )
{
return this.CommandLineArgs.Any( a => ( "-" + a ) == flagName || ( "/" + a ) == flagName );
}
}
/// <summary>Implements <see cref="IEntrypointInfo"/> by exposing data provided by <see cref="System.Environment"/>.</summary>
public class SystemEnvironmentEntrypointInfo : IEntrypointInfo
{
public String CommandLine => System.Environment.CommandLine;
public IReadOnlyList<String> CommandLineArgs => System.Environment.GetCommandLineArgs();
}
/// <summary>Implements <see cref="IEntrypointInfo"/> by exposing provided data.</summary>
public class SimpleEntrypointInfo : IEntrypointInfo
{
public SimpleEntrypointInfo( String commandLine, String[] commandLineArgs )
{
this.CommandLine = commandLine ?? throw new ArgumentNullException(nameof(commandLine));
this.CommandLineArgs = commandLineArgs ?? throw new ArgumentNullException(nameof(commandLineArgs));
}
public String CommandLine { get; }
public IReadOnlyList<String> CommandLineArgs { get; }
}
//
public static class Program
{
public static async Task Main( String[] args )
{
await Host.CreateDefaultBuilder( args )
.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.ConfigureServices((context, services) =>
{
services.AddHostedService<ConsoleHostedService>();
services.AddSingleton<IEntrypointInfo,SystemEnvironmentEntrypointInfo>()
})
.RunConsoleAsync();
}

对于自动化单元和集成测试,使用SimpleEntrypointInfo

CreateDefaultBuilderCommandLineConfigurationProvider添加到其配置提供程序中,但您通常不会直接访问它。相反,将IConfiguration参数添加到ConsoleHostedService构造函数中,您将自动从多个设置源接收设置,包括命令行参数:

internal sealed class ConsoleHostedService : IHostedService
{
public ConsoleHostedService(
IHostApplicationLifetime appLifetime,
IServiceProvider serviceProvider,
IConfiguration configuration)
{
// Get the value as a string
string argValueString = configuration["MyFirstArg"]
// Or if it's an integer
int argValueInt = configuration.GetValue<int>("MyFirstArg")
}
}

这确实要求您的命令行参数遵循这里定义的指定格式:

myfirststarg =12345
/myfirststarg 12345
—myfirststarg 12345

然而…

如果真的必须获取实际的命令行参数,如果您不介意依赖默认构建器的实现,您可以这样做:

创建自定义CommandLineConfigurationProvider类并公开其DataArgs属性:

public class ExposedCommandLineConfigurationProvider : CommandLineConfigurationProvider
{
public ExposedCommandLineConfigurationProvider(IEnumerable<string> args, IDictionary<string, string> switchMappings = null)
:base(args, switchMappings)
{
}
public new IDictionary<string, string> Data => base.Data;
public new IEnumerable<string> Args => base.Args;
}

然后在主程序中,将其添加到现有配置提供程序的列表中:

internal sealed class Program
{
private static async Task Main(string[] args)
{
await Host.CreateDefaultBuilder(args)
.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.ConfigureAppConfiguration(config => config
.Add(new ExposedCommandLineConfigurationSource { Args = args }))
.ConfigureServices((context, services) => services
.AddHostedService<SchedulerWorker>())
.RunConsoleAsync();
}
}

最后,从提供给您的consolehostdservice的IConfiguration中找出您的参数提供程序:

internal sealed class ConsoleHostedService : IHostedService
{
public ConsoleHostedService(
IHostApplicationLifetime appLifetime,
IServiceProvider serviceProvider,
IConfiguration configuration)
{
if (configuration is ConfigurationRoot configRoot)
{
var provider = configRoot.Providers.OfType<ExposedCommandLineConfigurationProvider>().Single();
var rawArgs = provider.Args;
var namedArgs = provider.Data;
}
else
{
// Handle this unlikely situation
}
}
}

但是对于一些可以简单完成的事情,这似乎是大量的工作(并且可能被默认构建器实现中的任何更改所破坏)。

最新更新