"correct" 创建没有后台服务的 .NET Core 控制台应用程序的方法
The "correct" way to create a .NET Core console app without background services
我正在构建一个简单的 .NET Core 控制台应用程序,它将从命令行读取基本选项,然后在没有用户交互的情况下执行和终止。我想利用 DI,这样我就可以使用 .NET Core 通用主机。
我发现构建控制台应用程序的所有示例都会创建一个 class,它要么实现 IHostedService,要么扩展 BackgroundService。然后 class 通过 AddHostedService 添加到服务容器,并通过 StartAsync 或 ExecuteAsync 启动应用程序的工作。但是,似乎在所有这些示例中,它们都在实现后台服务或循环运行或等待请求直到它被 OS 关闭或收到一些终止请求的其他应用程序。如果我只想要一个启动、执行其操作然后退出的应用程序怎么办?例如:
Program.cs:
namespace MyApp
{
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public static class Program
{
public static async Task Main(string[] args)
{
await CreateHostBuilder(args).RunConsoleAsync();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseConsoleLifetime()
.ConfigureLogging(builder => builder.SetMinimumLevel(LogLevel.Warning))
.ConfigureServices((hostContext, services) =>
{
services.Configure<MyServiceOptions>(hostContext.Configuration);
services.AddHostedService<MyService>();
services.AddSingleton(Console.Out);
});
}
}
MyServiceOptions.cs:
namespace MyApp
{
public class MyServiceOptions
{
public int OpCode { get; set; }
public int Operand { get; set; }
}
}
MyService.cs:
namespace MyApp
{
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
public class MyService : IHostedService
{
private readonly MyServiceOptions _options;
private readonly TextWriter _outputWriter;
public MyService(TextWriter outputWriter, IOptions<MyServiceOptions> options)
{
_options = options.Value;
_outputWriter = outputWriter;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("Starting work");
DoOperation(_options.OpCode, _options.Operand);
_outputWriter.WriteLine("Work complete");
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("StopAsync");
}
protected void DoOperation(int opCode, int operand)
{
_outputWriter.WriteLine("Doing {0} to {1}...", opCode, operand);
// Do work that might take awhile
}
}
}
此代码编译并运行得很好,产生以下输出:
Starting work
Doing 1 to 2...
Work complete
但是,在那之后,应用程序将一直等待,直到我按下 Ctrl+C。我知道我可以在工作完成后强制关闭应用程序,但在这一点上,我觉得我没有正确使用 IHostedService。它似乎是为重复出现的后台进程而设计的,而不是像这样的简单控制台应用程序。但是,在 DoOperation 可能需要 20-30 分钟的实际应用程序中,我想利用 StopAsync 方法在终止之前进行清理。我也知道我可以自己创建服务容器等等,但是 .NET Core 通用主机已经做了很多我想做的事情。 似乎 是编写控制台应用程序的正确方法,但如果不添加启动实际工作的托管服务,我如何让应用程序真正做 什么?
我推荐以下服务,而不是托管服务;
using (var host = CreateHostBuilder(args).Build())
{
await host.StartAsync();
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
// do work here / get your work service ...
lifetime.StopApplication();
await host.WaitForShutdownAsync();
}
I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly.
我同意这看起来确实很奇怪。实际上,我 总是 在所有 IHostedService
实施结束时停止应用程序。即使对于长 运行 服务器应用程序也是如此。如果托管服务停止(或出现故障),那么我明确地希望 应用程序结束。
当.NET Core推出时,确实感觉这个设计没有完成。设计的某些部分使托管服务及其应用程序可以拥有独立的生命周期,但由于它们无法重新启动,因此这在实践中没有用处。所以它最终感觉像是一个糟糕的设计,因为生命周期不能独立,但默认情况下它们是独立的。
我所有的托管服务最终都是 tying their lifetime to the application lifetime。
finally
{
_hostApplicationLifetime.StopApplication();
}
我正在构建一个简单的 .NET Core 控制台应用程序,它将从命令行读取基本选项,然后在没有用户交互的情况下执行和终止。我想利用 DI,这样我就可以使用 .NET Core 通用主机。
我发现构建控制台应用程序的所有示例都会创建一个 class,它要么实现 IHostedService,要么扩展 BackgroundService。然后 class 通过 AddHostedService 添加到服务容器,并通过 StartAsync 或 ExecuteAsync 启动应用程序的工作。但是,似乎在所有这些示例中,它们都在实现后台服务或循环运行或等待请求直到它被 OS 关闭或收到一些终止请求的其他应用程序。如果我只想要一个启动、执行其操作然后退出的应用程序怎么办?例如:
Program.cs:
namespace MyApp
{
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public static class Program
{
public static async Task Main(string[] args)
{
await CreateHostBuilder(args).RunConsoleAsync();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseConsoleLifetime()
.ConfigureLogging(builder => builder.SetMinimumLevel(LogLevel.Warning))
.ConfigureServices((hostContext, services) =>
{
services.Configure<MyServiceOptions>(hostContext.Configuration);
services.AddHostedService<MyService>();
services.AddSingleton(Console.Out);
});
}
}
MyServiceOptions.cs:
namespace MyApp
{
public class MyServiceOptions
{
public int OpCode { get; set; }
public int Operand { get; set; }
}
}
MyService.cs:
namespace MyApp
{
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
public class MyService : IHostedService
{
private readonly MyServiceOptions _options;
private readonly TextWriter _outputWriter;
public MyService(TextWriter outputWriter, IOptions<MyServiceOptions> options)
{
_options = options.Value;
_outputWriter = outputWriter;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("Starting work");
DoOperation(_options.OpCode, _options.Operand);
_outputWriter.WriteLine("Work complete");
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("StopAsync");
}
protected void DoOperation(int opCode, int operand)
{
_outputWriter.WriteLine("Doing {0} to {1}...", opCode, operand);
// Do work that might take awhile
}
}
}
此代码编译并运行得很好,产生以下输出:
Starting work
Doing 1 to 2...
Work complete
但是,在那之后,应用程序将一直等待,直到我按下 Ctrl+C。我知道我可以在工作完成后强制关闭应用程序,但在这一点上,我觉得我没有正确使用 IHostedService。它似乎是为重复出现的后台进程而设计的,而不是像这样的简单控制台应用程序。但是,在 DoOperation 可能需要 20-30 分钟的实际应用程序中,我想利用 StopAsync 方法在终止之前进行清理。我也知道我可以自己创建服务容器等等,但是 .NET Core 通用主机已经做了很多我想做的事情。 似乎 是编写控制台应用程序的正确方法,但如果不添加启动实际工作的托管服务,我如何让应用程序真正做 什么?
我推荐以下服务,而不是托管服务;
using (var host = CreateHostBuilder(args).Build())
{
await host.StartAsync();
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
// do work here / get your work service ...
lifetime.StopApplication();
await host.WaitForShutdownAsync();
}
I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly.
我同意这看起来确实很奇怪。实际上,我 总是 在所有 IHostedService
实施结束时停止应用程序。即使对于长 运行 服务器应用程序也是如此。如果托管服务停止(或出现故障),那么我明确地希望 应用程序结束。
当.NET Core推出时,确实感觉这个设计没有完成。设计的某些部分使托管服务及其应用程序可以拥有独立的生命周期,但由于它们无法重新启动,因此这在实践中没有用处。所以它最终感觉像是一个糟糕的设计,因为生命周期不能独立,但默认情况下它们是独立的。
我所有的托管服务最终都是 tying their lifetime to the application lifetime。
finally
{
_hostApplicationLifetime.StopApplication();
}