Xorcist
Xorcist

Reputation: 3311

.NET Core 2.0 IServiceCollection missing AddHostedService?

Trying to use:

Startup.cs

public void ConfigureServices(IServiceCollection services) {
  services.AddHostedService<LifetimeEvents>();
    .
    .
    .
}

where the LifeTimeEvents class inherits from IHostedService. I get this error:

'IServiceCollection' does not contain a definition for 'AddHostedService' and no extension method 'AddHostedService' accepting a first argument of type 'IServiceCollection' could be found (are you missing a using directive or an assembly reference?)

I can't seem to find the proper namespace to use or nuget package to include to get this working, but it worked out of the box in .NET Core 2.1, is this just not available in .NET Core 2.0? Is there any way to get it working?

UPDATE:

As a workaround I changed my code to use:

Startup.cs

public void ConfigureServices(IServiceCollection services) {
  services.AddSingleton<LifetimeEvents>();
    .
    .
    .
}

public void Configure(IApplicationBuilder appBuilder, IHostingEnvironment envHost, LifetimeEvents appEvents)  {
  appEvents.StartAsync(new CancellationToken(false));
    .
    .
    .
}

and that seems to have done the job. Doesn't answer my original question, and I'm not sure how "best practices" it is, but it did get me moving refactoring this .NET Core 2.0 app.

Upvotes: 1

Views: 2345

Answers (2)

Nicholas
Nicholas

Reputation: 1980

I believe this is a duplicate question to what I've answered before.

Where am I supposed to start persistent background tasks in ASP.NET Core?

Below is the answer, copy + pasted.

I believe you're looking for this

https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/

And i did a 2 hour self-proclaimed-award-winning hackathon against myself to learn abit of that.

https://github.com/nixxholas/nautilus

You can refer the injections here and implement the abstracts from there too.

Many MVC projects are not really required to operate persistent background tasks. This is why you don't see them baked into a fresh new project via the template. It's better to provide developers an interface to tap on and go ahead with it.

Also, with regards to opening that socket connection for such background tasks, I have yet to establish a solution for that. As far as I know/did, I was only able to broadcast payload to clients that are connected to my own socketmanager so you'll have to look elsewhere for that. I'll definitely beep if there is anything regarding websockets in an IHostedService.

Ok anyway here's what happens.

Put this somewhere in your project, its more of an interface for you to overload with to create your own task

/// Copyright(c) .NET Foundation.Licensed under the Apache License, Version 2.0.
    /// <summary>
    /// Base class for implementing a long running <see cref="IHostedService"/>.
    /// </summary>
    public abstract class BackgroundService : IHostedService, IDisposable
    {
        protected readonly IServiceScopeFactory _scopeFactory;
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts =
                                                       new CancellationTokenSource();

        public BackgroundService(IServiceScopeFactory scopeFactory) {
            _scopeFactory = scopeFactory;
        }

        protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

        public virtual Task StartAsync(CancellationToken cancellationToken)
        {
            // Store the task we're executing
            _executingTask = ExecuteAsync(_stoppingCts.Token);

            // If the task is completed then return it,
            // this will bubble cancellation and failure to the caller
            if (_executingTask.IsCompleted)
            {
                return _executingTask;
            }

            // Otherwise it's running
            return Task.CompletedTask;
        }

        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            // Stop called without start
            if (_executingTask == null)
            {
                return;
            }

            try
            {
                // Signal cancellation to the executing method
                _stoppingCts.Cancel();
            }
            finally
            {
                // Wait until the task completes or the stop token triggers
                await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                              cancellationToken));
            }
        }

        public virtual void Dispose()
        {
            _stoppingCts.Cancel();
        }
    }

Here's how you can actually use it

public class IncomingEthTxService : BackgroundService
    {
        public IncomingEthTxService(IServiceScopeFactory scopeFactory) : base(scopeFactory)
        {
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {

            while (!stoppingToken.IsCancellationRequested)
            {
                using (var scope = _scopeFactory.CreateScope())
                {
                    var dbContext = scope.ServiceProvider.GetRequiredService<NautilusDbContext>();

                    Console.WriteLine("[IncomingEthTxService] Service is Running");

                    // Run something

                    await Task.Delay(5, stoppingToken);
                }
            }
        }
    }

If you noticed, there's a bonus there. You'll have to use a servicescope in order to access db operations because its a singleton.

Inject your service in

// Background Service Dependencies
            services.AddSingleton<IHostedService, IncomingEthTxService>();

Upvotes: 0

Nkosi
Nkosi

Reputation: 247088

is this just not available in .NET Core 2.0?

ServiceCollectionHostedServiceExtensions.AddHostedService(IServiceCollection) Method as shown in the API reference

Applies to
ASP.NET Core
2.1

However the source code is available on GitHub. You can easily check it out there and copy a local version to your 2.0 project

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

Source code

Ideally you could just update the project to 2.1 where the extension becomes available.

Upvotes: 2

Related Questions