在 ASP.NET Core 中注册 HostedService 的正确方法。 AddHostedService 与 AddSingleton

问题描述 投票:0回答:4

在 ASP.NET Core 2.1 中注册自定义托管服务的正确方法是什么?例如,我有一个源自 BackgroundService 的自定义托管服务,名为

MyHostedService
。我该如何注册?

public IServiceProvider ConfigureServices(IServiceCollection services)
{           
    //...
    services.AddSingleton<IHostedService, MyHostedService>();
}

public IServiceProvider ConfigureServices(IServiceCollection services)
{           
    //...
    services.AddHostedService<MyHostedService>();
}

这里我们可以看到第一种情况,但是这里还有第二种情况。

这些方法相同吗?

c# asp.net-core .net-core dependency-injection asp.net-core-hosted-services
4个回答
65
投票

更新

过去,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 class MyHostedService : IHostedService { private readonly IServiceScopeFactory scopeFactory; public MyHostedService(IServiceScopeFactory scopeFactory) { this.scopeFactory = scopeFactory; } public void DoWork() { using (var scope = scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>(); … } } … }

更新

33
投票

在 .Net Core 2.2 和 3.1 之间,行为发生了变化,AddHostedService 现在添加了 Singleton,而不是之前的 Transient 服务。 信用 -

LeonG

评论 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>();

一个巨大的区别是 

19
投票
是懒惰的,而

AddHostedService()

 是渴望的。
使用 
AddSingleton()

添加的服务将在第一次注入类构造函数时被实例化。这对于大多数服务来说都很好,但如果它确实是您想要的后台服务,您可能希望它立即启动。

使用 
AddHostedService()

添加的服务将立即实例化,即使没有其他类希望将其注入到其构造函数中。这是一直运行的后台服务的典型情况。

此外,似乎您
不能

将添加了

AddHostedService()的服务注入到另一个类中。


一个主要区别是

0
投票
将允许创建同一后台服务的多个实例,而

AddHostedService()

 将忽略重复项。
拥有同一后台服务的多个实例很有用的一个用例是,如果您想让多个工作人员从队列中提取工作。以下是如何创建同一后台服务的多个实例的示例:

for (int i = 0; i < workerCount; i++) { services.AddSingleton<IHostedService, WorkerBackgroundService>(); }


© www.soinside.com 2019 - 2024. All rights reserved.