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

Den*_*kin 20 c# .net-core asp.net-core asp.net-core-2.1 asp.net-core-hosted-services

在ASP.NET Core 2.1中注册自定义托管服务的正确方法是什么?例如,我已经定制托管源自服务BackgroundService命名MyHostedService.我该如何注册?

public IServiceProvider ConfigureServices(IServiceCollection services)
{           
    //...
    services.AddSingleton<IHostedService, MyHostedService>();
}
Run Code Online (Sandbox Code Playgroud)

要么

public IServiceProvider ConfigureServices(IServiceCollection services)
{           
    //...
    services.AddHostedService<MyHostedService>();
}
Run Code Online (Sandbox Code Playgroud)

在这里我们可以看到第一种情况,但这里有第二种情况.

这些方法是否相同?

Pan*_*vos 18

使用 AddHostedService

托管服务不仅仅是单一服务.运行时"知道"它,可以告诉它通过调用启动StartAsync或通过调用,StopAsync()例如应用程序池被回收.运行时可以等待托管服务在Web应用程序本身终止之前完成.

正如文档所解释的那样,可以通过在托管服务的worker方法中创建作用域来使用作用域服务.对于瞬态服务也是如此.

为此,必须在托管服务的构造函数中注入IServicesProvider或IServiceScopeFactory,并用于创建范围.

从文档中借用,服务的构造函数和worker方法可以如下所示:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

此相关问题显示如何在托管服务中使用瞬态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>();
            …
        }
    }
    …
}
Run Code Online (Sandbox Code Playgroud)

  • 小更正:托管服务实际上不是单例服务。对于某些具体类,您可以根据需要多次调用 AddHostedService。运行时将启动服务的多个实例。例如,这可以与 BackgroundService 一起使用来设置工作池。 (7认同)

Chr*_*vén 17

一个巨大的区别是AddSingleton()懒惰和AddHostedService()渴望。

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

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

另外,似乎您无法将添加的服务注入AddHostedService()到另一个类中。


Nko*_*osi 8

它们相似但不完全

AddHostedService是的一部分Microsoft.Extensions.Hosting.Abstractions.

它属于 Microsoft.Extensions.Hosting.AbstractionsServiceCollectionHostedServiceExtensions

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,它使用的是Transient生命时间范围而不是Singleton

在框架内部,所有托管服务都添加到另一个服务(HostedServiceExecutor)

public HostedServiceExecutor(ILogger<HostedServiceExecutor> logger, 
    IEnumerable<IHostedService> services) //<<-- note services collection
{
    _logger = logger;
    _services = services;
}
Run Code Online (Sandbox Code Playgroud)

在启动时,它是通过WebHost构造函数的单例.

_applicationServiceCollection.AddSingleton<HostedServiceExecutor>();
Run Code Online (Sandbox Code Playgroud)

  • 在 .Net Core 2.2 和 3.1 之间,行为发生了变化,AddHostedService 现在添加了 Singleton,而不是之前的 Transient 服务。 (6认同)
  • @DenisBabarykin是的,这是准确的. (2认同)
  • @ johnny5 scoped很特别.单例仍然可以调用瞬态生存依赖.从技术上讲,瞬态变为单身,因为实例将存在于单身中. (2认同)