介绍
在本文中,我将举一个例子,说明如何使用SimpleSoft.Hosting包实现更高级的控制台应用程序 。这个想法是通过利用最近的Microsoft.Extensions。*包,但没有太多的样板代码,更容易设置依赖注入,记录和导入应用程序设置。这些概念类似于Microsoft.AspNetCore.Hosting包,但没有不必要的ASP.NET依赖关系,并且在创建Windows服务,实用工具,自主托管WCF应用程序或您可能拥有的其他需求时应该很有用。
在这种情况下,该示例是一个简单的控制台应用程序,它执行一些定期的Web请求。从应用程序设置读取地址和批处理延迟,NLog用于登录文件,AutoFac是依赖关系注入容器。
使用代码
该应用程序主要分为三类:
Program
- 控制台应用程序的入口点;HttpMonitorStartup
- 配置类,注册和配置所有必需的依赖项;HttpMonitorHost
- 主机入门点类;
程序
由于这是一个控制台应用程序,我将首先解释入门点类:
- public class Program
- {
- private static readonly CancellationTokenSource TokenSource;
-
- static Program()
- {
- TokenSource = new CancellationTokenSource();
- Console.CancelKeyPress += (sender, args) => {
- TokenSource.Cancel();
- args.Cancel = true;
- };
- }
-
- public static int Main(string[] args) => MainAsync(args, TokenSource.Token).ConfigureAwait(false).GetAwaiter().GetResult();
-
- private static async Task<int> MainAsync(string[] args, CancellationToken ct)
- {
- ExecutionResult result;
-
- var loggerFactory = new LoggerFactory()
- .AddConsole(LogLevel.Trace, true);
-
- var logger = loggerFactory.CreateLogger<Program>();
-
- try {
- logger.LogDebug("Preparing the host builder");
-
- using (var hostBuilder = new HostBuilder("HTTPMONITOR_ENVIRONMENT")
- .UseLoggerFactory(loggerFactory)
- .UseStartup<HttpMonitorStartup>()
- .ConfigureConfigurationBuilder(p => {
- p.Builder.AddCommandLine(args);
- }))
- {
- await hostBuilder.RunHostAsync<HttpMonitorHost>(ct);
- }
-
- result = ExecutionResult.Success;
- }
- catch (TaskCanceledException)
- {
- logger.LogWarning("The application execution was canceled");
- result = ExecutionResult.Canceled;
- }
- catch (Exception e)
- {
- logger.LogCritical(0, e, "Unexpected exception has occurred");
- result = ExecutionResult.Failed;
- }
- logger.LogInformation("Application terminated [{result}]. Press <enter> to exit...", result);
- Console.ReadLine();
-
- return (int) result;
- }
-
- private enum ExecutionResult
- {
- Success = 0,
- Failed = 1,
- Canceled = 2 }
- }
在ILoggerFactory
最初的建设者之外创建的,因为我想至少包括在控制台每个日志。这不是一个要求,因为如果没有设置,构建器将在构建主机时使用默认工厂,没有任何提供者。我还使用环境变量 HTTPMONITOR_ENVIRONMENT来决定主机在哪个环境中运行。
在这个例子中,我使用一个自定义IHostStartup
类来注册和配置所有的应用依赖。有一些方法可以将任何支持的配置处理程序直接添加到构建器中,但是我更希望将代码保存在主方法简洁中。请注意,我附加一个第二个IConfigurationBuilder
处理程序来将参数包含在配置中,但是我可以很容易地将它们作为参数传递给启动类。
然后,我只是运行主机。如果类没有手动注册到服务集合中,构建器将自动添加一个作用域生命周期,因此它将包含在IHostRunContext<THost>
实例中。
当建立主机时,管道如下:
- 处理程序
IConfigurationBuilder
,可以访问IHostingEnvironment
; - 处理程序
IConfigurationRoot
,可以访问IHostingEnvironment
; - 处理程序
ILoggerFactory
,可以访问IConfiguration
和IHostingEnvironment
; - 处理程序
IServiceCollection
,可以访问ILoggerFactory
,IConfiguration
和IHostingEnvironment
; - 构建
IServiceProvider
具有访问IServiceCollection
,ILoggerFactory
,IConfiguration
和IHostingEnvironment
; - 的处理程序
IServiceProvider
能够访问所述,IServiceProvider
,ILoggerFactory
,IConfiguration
和IHostingEnvironment
;
注意实例 ILoggerFactory
,IConfigurationRoot
并IHostingEnvironment
自动注册到容器中。
备注:如果要配置记录器工厂,配置或任何其他要求,并且需要知道当前的环境或目录,并希望确保与主机构建器使用相同的属性,则可以始终HostingEnvironment
通过使用构造函数创建 实例或静态方法 HostingEnvironment.BuildDefault
,并将环境作为构建器的参数传递。例:
- var loggerFactory = new LoggerFactory()
- .AddConsole(LogLevel.Trace, true)
- .AddNLog(); var env = HostingEnvironment.BuildDefault("HTTPMONITOR_ENVIRONMENT"); var nlogConfigFile = env.ContentRootFileProvider.GetFileInfo($"nlog.{env.Name}.config"); if (!nlogConfigFile.Exists)
- nlogConfigFile = env.ContentRootFileProvider.GetFileInfo("nlog.config");
-
- loggerFactory.ConfigureNLog(nlogConfigFile.PhysicalPath); var builder = new HostBuilder(env).UseLoggerFactory(loggerFactory);
HttpMonitorStartup
尽管不需要,一个启动类可以用来聚合所有的主机设置:
- public class HttpMonitorStartup : HostStartup
- {
- public override void ConfigureConfigurationBuilder(IConfigurationBuilderParam param)
- {
- param.Builder
- .SetBasePath(param.Environment.ContentRootPath)
- .AddJsonFile("appsettings.json", false, true)
- .AddJsonFile($"appsettings.{param.Environment.Name}.json", true, true)
- .AddEnvironmentVariables();
- }
-
- public override void ConfigureLoggerFactory(ILoggerFactoryHandlerParam param)
- {
- var nlogConfigFile = param.Environment.ContentRootFileProvider.GetFileInfo($"nlog.{param.Environment.Name}.config");
- if (!nlogConfigFile.Exists)
- nlogConfigFile = param.Environment.ContentRootFileProvider.GetFileInfo("nlog.config");
-
- param.LoggerFactory
- .AddNLog()
- .ConfigureNLog(nlogConfigFile.PhysicalPath);
- }
-
- public override void ConfigureServiceCollection(IServiceCollectionHandlerParam param)
- {
- param.ServiceCollection
- .AddOptions()
- .Configure<HttpMonitorOptions>(param.Configuration)
- .AddSingleton(s => s.GetRequiredService<IOptions<HttpMonitorOptions>>().Value);
-
- param.ServiceCollection
- .AddSingleton(s => new HttpClient())
- .AddSingleton<IUrlRequester, UrlRequester>();
- }
-
- public override IServiceProvider BuildServiceProvider(IServiceProviderBuilderParam param)
- {
- var container = new ContainerBuilder();
- container.Populate(param.ServiceCollection);
- return new AutofacServiceProvider(container.Build());
- }
- }
就像ASP.NET Core一样,您可以有条件地加载设置,配置应用程序日志记录或替换默认容器实现。抽象类 HostStartup
使得更容易仅覆盖您的设置所需的内容。
HttpMonitorHost
在依赖注入容器的范围内运行的应用程序主机,这意味着您现在可以像任何ASP.NET应用程序一样布线。在这种情况下,我只需获取应用程序选项并开始制作HTTP请求。
- public class HttpMonitorHost : IHost
- {
- private readonly HttpMonitorOptions _options;
- private readonly IUrlRequester _urlRequester;
- private readonly ILogger<HttpMonitorHost> _logger;
-
- public HttpMonitorHost(HttpMonitorOptions options, IUrlRequester urlRequester, ILogger<HttpMonitorHost> logger)
- {
- _options = options;
- _urlRequester = urlRequester;
- _logger = logger;
- }
-
- public async Task RunAsync(CancellationToken ct)
- {
- for (var i = 0; i < _options.MaximumBatches; i++)
- {
- _logger.LogDebug("Sending a batch of requests");
- await Task.WhenAll(_options.Urls.Select(url => _urlRequester.RequestAsync(url, ct)));
-
- _logger.LogDebug("Waiting for the next batch time");
- await Task.Delay(_options.BatchDelayInMs, ct);
- }
-
- _logger.LogDebug("Maximum of {maximumBatches} batches have been made", _options.MaximumBatches);
- }
- }
执行
运行应用程序时,应该看到如下输出:
结论
本文介绍了如何使用SimpleSoft.Hosting包开发高级控制台应用程序 。由于它使用最新的Microsoft扩展程序包,第三方的支持是伟大的,使其成为一个要记住的选项。