Как правильно зарегистрировать настраиваемую размещенную службу в ASP.NET Core 2.1? Например, у меня есть настраиваемая размещенная служба, производная от Справочная информация, с именем MyHostedService. Как мне его зарегистрировать?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddSingleton<IHostedService, MyHostedService>();
}
или же
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddHostedService<MyHostedService>();
}
?
Здесь мы можем видеть первый случай, но здесь есть второй случай.
Эти методы равны?
Are they equal секунды, которые вызывают первый изнутри, но как переходный, а не синглтон
Более того, какой из них является синглтоном, а какой нет. Размещенные службы получают особую обработку со стороны среды выполнения с помощью методов StartAsync, StopAsync. Использование ограниченных / переходных объектов является возможно при использовании областей видимости





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, его время жизни будет равно времени жизни приложения, верно?
@Nkosi, как временные сервисы добавляются в синглтон, разве в этом случае не должен бросать инжектор зависимостей?
@DenisBabarykin да, это будет точно.
@ johnny5 не обязательно. Переходный процесс все равно будет разрешен. Всего один раз синглтоном. :) Transient lifetime services are created each time they're requested
@Nkosi они вручную запрашивают службу, я думал, что поведение изменилось между .Net-Core 1 и .Net-Core 2
@ johnny5 scoped особенный. синглтон по-прежнему может вызывать временную зависимость времени жизни. Технически переходный процесс сам становится синглтоном, поскольку экземпляр будет жить в синглтоне.
@Nkosi, О, это только для области видимости, Спасибо за это разъяснение, я думал, что это все жизни
Время жизни @ johnny5 в области видимости длится только до тех пор, пока время жизни запроса. Вот почему к нему относятся иначе.
Где-то между .Net Core 2.2 и 3.1 поведение изменилось, AddHostedService теперь добавляет Singleton вместо предыдущей службы Transient.
Обновлять
В прошлом 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 Я не имел в виду, что это так, я отвечал на вопрос. Но теперь похоже, что размещенные сервисы находятся singleletons
Теперь он работает так же? Можно ли будет это сделать через Factory и ActivatorUtilites.CreateInstance?
более свежие документы рекомендовали вариант секунд