Правильный способ регистрации HostedService в ASP.NET Core. AddHostedService против AddSingleton

Как правильно зарегистрировать настраиваемую размещенную службу в ASP.NET Core 2.1? Например, у меня есть настраиваемая размещенная служба, производная от Справочная информация, с именем MyHostedService. Как мне его зарегистрировать?

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

или же

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

?

Здесь мы можем видеть первый случай, но здесь есть второй случай.

Эти методы равны?

более свежие документы рекомендовали вариант секунд

Nkosi 23.07.2018 15:53
Are they equal секунды, которые вызывают первый изнутри, но как переходный, а не синглтон
Nkosi 23.07.2018 15:54

Более того, какой из них является синглтоном, а какой нет. Размещенные службы получают особую обработку со стороны среды выполнения с помощью методов StartAsync, StopAsync. Использование ограниченных / переходных объектов является возможно при использовании областей видимости

Panagiotis Kanavos 23.07.2018 16:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
41
3
31 390
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обновлять

Somewhere between .Net Core 2.2 and 3.1 the behavior has changed, AddHostedService is now adding a Singleton instead of the previous Transient service. Credit - 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>();

Это значит, что несмотря на то, что сервис зарегистрирован как scoped внутри AddHostedService, его время жизни будет равно времени жизни приложения, верно?

Denis Babarykin 23.07.2018 16:05

@Nkosi, как временные сервисы добавляются в синглтон, разве в этом случае не должен бросать инжектор зависимостей?

johnny 5 23.07.2018 16:06

@DenisBabarykin да, это будет точно.

Nkosi 23.07.2018 16:09

@ johnny5 не обязательно. Переходный процесс все равно будет разрешен. Всего один раз синглтоном. :) Transient lifetime services are created each time they're requested

Nkosi 23.07.2018 16:11

@Nkosi они вручную запрашивают службу, я думал, что поведение изменилось между .Net-Core 1 и .Net-Core 2

johnny 5 23.07.2018 16:24

@ johnny5 scoped особенный. синглтон по-прежнему может вызывать временную зависимость времени жизни. Технически переходный процесс сам становится синглтоном, поскольку экземпляр будет жить в синглтоне.

Nkosi 23.07.2018 16:27

@Nkosi, О, это только для области видимости, Спасибо за это разъяснение, я думал, что это все жизни

johnny 5 23.07.2018 16:28

Время жизни @ johnny5 в области видимости длится только до тех пор, пока время жизни запроса. Вот почему к нему относятся иначе.

Nkosi 23.07.2018 16:29

Где-то между .Net Core 2.2 и 3.1 поведение изменилось, AddHostedService теперь добавляет Singleton вместо предыдущей службы Transient.

LeonG 10.06.2020 12:56

Обновлять

В прошлом HostedService был долговременным переходным процессом, эффективно действующим как одноэлементный объект. Начиная с .NET Core 3.1 это настоящий синглтон.


Используйте AddHostedService

Размещенная служба - это больше, чем просто одноэлементная служба. Среда выполнения «знает» об этом, может сказать ей начать, вызвав StartAsync, или остановиться, вызвав StopAsync(), когда, например, пул приложений перезапускается. Среда выполнения может дождаться завершения работы размещенной службы до завершения работы самого веб-приложения.

Как документация объясняет, служба может с заданной областью может использоваться путем создания области внутри рабочего метода размещенной службы. То же самое и с временными услугами.

Для этого 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>();
            …
        }
    }
    …
}

Незначительное исправление: размещенная служба на самом деле не является одноэлементной службой. Вы можете вызывать AddHostedService сколько угодно раз для конкретного класса. Среда выполнения запустит несколько экземпляров службы. Это можно использовать с BackgroundService, например, для настройки пула рабочих.

kayjtea 12.02.2019 17:51

@kayjtea Я не имел в виду, что это так, я отвечал на вопрос. Но теперь похоже, что размещенные сервисы находятся singleletons

Panagiotis Kanavos 19.01.2021 09:47

Теперь он работает так же? Можно ли будет это сделать через Factory и ActivatorUtilites.CreateInstance?

xSx 16.06.2021 16:13

Другие вопросы по теме