Search code examples
c#asp.net-core.net-coredependency-injectionasp.net-core-hosted-services

Proper way to register HostedService in ASP.NET Core. AddHostedService vs AddSingleton


What is the proper way to register a custom hosted service in ASP.NET Core 2.1? For example, I have a custom hosted service derived from BackgroundService named MyHostedService. How should I register it?

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

or

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

?

Here we can see the first case, but here there is a second case.

Are these methods equal?


Solution

  • Update

    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 - Comment by 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;
        }
    }
    

    Reference ServiceCollectionHostedServiceExtensions


    Original Answer

    They are similar but not completely

    AddHostedService is part of Microsoft.Extensions.Hosting.Abstractions.

    It belongs to Microsoft.Extensions.Hosting.Abstractions in the ServiceCollectionHostedServiceExtensions class

    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>();
        }
    }
    

    Note it is using Transient life time scope and not Singleton

    Internally the framework add all the hosted services to another service (HostedServiceExecutor)

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

    at startup that is a singleton via the WebHost Constructor.

    _applicationServiceCollection.AddSingleton<HostedServiceExecutor>();