在 ASP.NET Core 2.1 中注册自定义托管服务的正确方法是什么?例如,我有一个源自 BackgroundService 的自定义托管服务,名为
MyHostedService
。我该如何注册?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddSingleton<IHostedService, MyHostedService>();
}
或
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddHostedService<MyHostedService>();
}
?
这些方法相同吗?
更新
过去,HostedService 是一种长期存在的瞬态服务,实际上充当单例。从 .NET Core 3.1 开始它是一个真正的单例。
使用
AddHostedService
托管服务不仅仅是单例服务。运行时“知道”它,可以告诉它通过调用
StartAsync
启动或通过调用 StopAsync()
停止,例如每当应用程序池被回收时。运行时可以在 Web 应用程序本身终止之前等待托管服务完成。
正如文档所解释的那样,作用域服务可以通过在托管服务的辅助方法中创建作用域来使用。对于临时服务也是如此。 为此,必须将 IServicesProvider 或 IServiceScopeFactory 注入托管服务的构造函数中并用于创建范围。
借用文档,服务的构造函数和工作方法可以如下所示:
public IServiceProvider Services { get; }
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
private void DoWork()
{
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
scopedProcessingService.DoWork();
}
}
这个相关问题展示了如何在托管服务中使用瞬态 DbContext: 更新
参考的评论
public static class ServiceCollectionHostedServiceExtensions { /// <summary> /// Add an <see cref="IHostedService"/> registration for the given type. /// </summary> /// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam> /// <param name="services">The <see cref="IServiceCollection"/> to register with.</param> /// <returns>The original <see cref="IServiceCollection"/>.</returns> public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services) where THostedService : class, IHostedService { services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>()); return services; } /// <summary> /// Add an <see cref="IHostedService"/> registration for the given type. /// </summary> /// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam> /// <param name="services">The <see cref="IServiceCollection"/> to register with.</param> /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param> /// <returns>The original <see cref="IServiceCollection"/>.</returns> public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services, Func<IServiceProvider, THostedService> implementationFactory) where THostedService : class, IHostedService { services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService>(implementationFactory)); return services; } }
ServiceCollectionHostedServiceExtensions
AddHostedService
Microsoft.Extensions.Hosting.Abstractions
的一部分。
属于
Microsoft.Extensions.Hosting.Abstractions
类中的
ServiceCollectionHostedServiceExtensions
using Microsoft.Extensions.Hosting;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionHostedServiceExtensions
{
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
=> services.AddTransient<IHostedService, THostedService>();
}
}
请注意,它使用的是 Transient
生命周期范围,而不是 Singleton
框架内部将所有托管服务添加到另一个服务(
HostedServiceExecutor
)
public HostedServiceExecutor(ILogger<HostedServiceExecutor> logger,
IEnumerable<IHostedService> services) //<<-- note services collection
{
_logger = logger;
_services = services;
}
在启动时,通过 WebHost 构造函数是一个单例。
_applicationServiceCollection.AddSingleton<HostedServiceExecutor>();
AddHostedService()
是渴望的。
使用
AddSingleton()
添加的服务将在第一次注入类构造函数时被实例化。这对于大多数服务来说都很好,但如果它确实是您想要的后台服务,您可能希望它立即启动。
使用
AddHostedService()
添加的服务将立即实例化,即使没有其他类希望将其注入到其构造函数中。这是一直运行的后台服务的典型情况。
此外,似乎您不能将添加了
AddHostedService()
的服务注入到另一个类中。
AddHostedService()
将忽略重复项。
拥有同一后台服务的多个实例很有用的一个用例是,如果您想让多个工作人员从队列中提取工作。以下是如何创建同一后台服务的多个实例的示例:
for (int i = 0; i < workerCount; i++)
{
services.AddSingleton<IHostedService, WorkerBackgroundService>();
}