.NET Core 2.0 IServiceCollection missing AddHostedService? - c#

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.

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.

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

Related

OWIN extension OAuthAuthorizationServerProvider support in .NET Core?

We have migrated from .NET framework to .NET 6, however after conversion the reference libraries like "Microsoft.Owin.Security.OAuth" are not supported so I'm not able to use "OAuthAuthorizationServerProvider", we need the custom provider to validate credentials against AD, could you please help how to achieve the same in .NET core, same applies for Refresh token.
Apart from that, is OWIN cross platform? if not, what is the latest or widely used authentication mechanism?
public class AuthorizationProvider : OAuthAuthorizationServerProvider
{
public AuthorizationProvider()
{
// Code
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
// token endpoint is called from configuration service and ui.
// source parameter added to identify from where endpoint is called
var source = context.Parameters.Where(f => f.Key == "source").Select(f => f.Value).SingleOrDefault();
if (source != null && !string.IsNullOrEmpty(source[0]))
{
context.OwinContext.Set<string>("source", source[0]);
}
}
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
/// <summary>
/// There are two create methods in this class as interface expects both to be implemented
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
Create(context);
}
}

.NET Core stop HostedService in the Integration test

I have .NET Core web API project, for some reasons, we created a background service in this project and start running the background service while the application is started.
So, we created a BackgroundWorkderService, which inherited from BackgroundService (Microsoft.Extensions.Hosting) like below:
public class BackgroundWorkerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await DoWork(stoppingToken);
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
await ExecuteAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
To run it while the application starts, I added the background service to the hosted service in Program.cs as below:
.ConfigureServices(services =>
services.AddHostedService<BackgroundWorkerService>());
Now, we need to create an integration test, and we want to stop the background service while we are running the integration test.
Does anyone know how to stop it in the integration test? I have tried to remove the service from ConfigureTestServices, but no luck with it, the background service still runs when the integration test starts.
You should be able to, as you mentioned in the question, remove the service in ConfigureTestServices. However, exactly how we specify which service to remove is the key here.
In my experience, you need to find the ServiceDescriptor in question rather than create a new one with the expected values (see note below for why). I use LINQ and the implementation type of the service (in your case, BackgroundWorkerService) to easily find the existing ServiceDescriptor. Then I can remove it from the list, which means it won't be started when CreateClient() is called on the WebApplicationFactory.
Looks something like this:
builder.ConfigureTestServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundWorkerService));
services.Remove(descriptor);
}
A really nice benefit of this approach is that it keeps test logic out of the production code and entirely in the setup/fixture for your tests.
Note on IServiceCollection removals: With a bit of poking, I believe this is because ServiceDescriptor doesn't provide an equality or comparison method other than Object.Equals, which falls back to reference equality. So even if all the values are the same in a new ServiceDescriptor, they would different objects and thus won't be found and removed from the service list.
I found a solution to put the background service register in a condition as below.
Edit the Program.cs file as below in the section of registering your background service:
.ConfigureServices(services =>
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "INTEGRATION")
{
services.AddHostedService<BackgroundWorkerService>();
}
});
Then try to change the variable to be INTEGRATION from where you need.
Other answers are correct, but I wanted to add a little supplement as I found why RemoveAll could still be used.
According to the dotnet source code the RemoveAll method uses ServiceType and not ImplementationType to find the service.
/// <summary>
/// Removes all services of type <paramref name="serviceType"/> in <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="collection">The <see cref="IServiceCollection"/>.</param>
/// <param name="serviceType">The service type to remove.</param>
/// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection RemoveAll(this IServiceCollection collection, Type serviceType)
{
ThrowHelper.ThrowIfNull(serviceType);
for (int i = collection.Count - 1; i >= 0; i--)
{
ServiceDescriptor? descriptor = collection[i];
if (descriptor.ServiceType == serviceType)
{
collection.RemoveAt(i);
}
}
return collection;
}
This means that we can't use actual service type of BackgroundWorkerService (as in the question) but instead the type it was registered with which is IHostedService
This means that this code can be used to nuke all background workers from existence.
services.RemoveAll<IHostedService>();
Unfortunately this removes also some workers that are registered by the asp.net framework, and they need to be reintroduced.
I faced same issue so I've extended WebApplicationFactory class and removed hosted service before by overriding CreateHost
public class CustomWebApplicationFactory<T> : WebApplicationFactory<T>
where T : class
{
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundService));
services.Remove(descriptor);
});
return base.CreateHost(builder);
}
}

Quartz job does not run with autofac and a constructor with a service

I have the Quartz Job shown below
public class ExtractTradesJob: IJob
{
private ITradeExtractor _tradeExtractor;
public ExtractTradesJob(ITradeExtractor tradeExtractor)
{
_tradeExtractor = tradeExtractor;
}
public async Task GetTradesAsync(DateTime dateTime)
{
Console.WriteLine(dateTime);
}
void IJob.Execute(IJobExecutionContext context)
{
Task.Run(async () => await GetTradesAsync(DateTime.Now));
}
}
I have 2 issues
As I need to use .NET 4.5, Async support is not available out of the box so is the method I have used for calling my async method correct?
It appears as though if I have a constructor as in my case, the job does not fire. I have checked and I know the the ITradeExtractor service is registered correctly. So how can I have a job that takes a service in its constructor? If I remove the constructor, my Execute method is called correctly
I am using AutoFac 3.5.2 and Quartz 2.6.2 and AutoFac.Extras.Quartz 3.5.0
I am using the code below to setup AutoFac
public static void RegisterWithAutofac(ContainerBuilder builder)
{
builder.RegisterType<TradeExtractor>()
.As<ITradeExtractor>()
.SingleInstance();
builder.Register(x => new StdSchedulerFactory().GetScheduler()).As<IScheduler>();
}
I know these are old packages, but my limitation of having to use .NET 4.5 means this is out of my control
Paul

Correct way to start a BackgroundService in ASP.NET Core

I have implemented a BackgroundService in an ASP.NET Core 2.1 application:
public class MyBackgroundService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
await DoSomethingAsync();
await Task.Delay(10 * 1000);
}
return Task.CompletedTask;
}
}
I have registered it in my ConfigureServices() method:
services.AddSingleton<MyBackgroundService>();
I am currently (reluctantly) starting it by calling (and not awaiting) the StartAsync() method from within the Configure() method:
app.ApplicationServices.GetService<SummaryCache>().StartAsync(new CancellationToken());
What is the best practice method for starting the long running service?
Explicitly calling StartAsync is not needed.
Calling
services.AddSingleton<MyBackgroundService>();
won't work since all service implementations are resolved via DI through IHostedService interface.
edit:
e.g.
svcProvider.GetServices<IHostedService>() -> IEnumerable<IHostedService>
You need to call either:
services.AddSingleton<IHostedService, MyBackgroundService>();
or
services.AddHostedService<MyBackgroundService>();
edit:
AddHostedService also registers an IHostedService: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionhostedserviceextensions.addhostedservice?view=aspnetcore-2.2

ASP.NET Core MVC Loading Session Asynchronously

I have been reading the official Session and application state documentation and have stumbled upon the following paragraph:
Loading Session asynchronously
The default session provider in ASP.NET Core loads the session record
from the underlying IDistributedCache store asynchronously only if the
ISession.LoadAsync method is explicitly called before the TryGetValue,
Set, or Remove methods. If LoadAsync is not called first, the
underlying session record is loaded synchronously, which could
potentially impact the ability of the app to scale.
To have applications enforce this pattern, wrap the
DistributedSessionStore and DistributedSession implementations with
versions that throw an exception if the LoadAsync method is not called
before TryGetValue, Set, or Remove. Register the wrapped versions in
the services container.
The wrapping itself is not an issue for me, but in order to implement it, I need:
Reference to the original implementation
Registering the wrapped version
Currently, I have created the following wrapper class:
public class WrappedDistributedSession : ISession
{
private DistributedSession _service;
private bool loaded = false;
public WrappedDistributedSession(DistributedSession service)
{
_service = service;
}
public bool IsAvailable => _service.IsAvailable;
public string Id => _service.Id;
public IEnumerable<string> Keys => _service.Keys;
public void Clear() => _service.Clear();
public Task CommitAsync() => _service.CommitAsync();
public Task LoadAsync()
{
loaded = true;
return _service.LoadAsync();
}
public void Remove(string key)
{
if(loaded)
{
_service.Remove(key);
} else
{
throw new Exception();
}
}
public void Set(string key, byte[] value)
{
if (loaded)
{
_service.Set(key, value);
}
else
{
throw new Exception();
}
}
public bool TryGetValue(string key, out byte[] value)
{
if (loaded)
{
return _service.TryGetValue(key, out value);
}
else
{
throw new Exception();
}
}
}
And I have registered it in the Startup.ConfigureServices
services.AddScoped<ISession, WrappedDistributedSession>();
Obviously, since I am writing this question, my solution does not work. Where did I go wrong and how does one "Register the wrapped versions in the services container"?
Use at your risk. This seems to work in Configure method just after sessions.
This solution is an adaptation from this unit test:
https://github.com/dotnet/aspnetcore/blob/cd0eab88eaa230fa276c27ab5dc71ea267efe14f/src/Middleware/Session/test/SessionTests.cs#L654-L656
app.UseSession();
app.Use(async (context, next) =>
{
await context.Session.LoadAsync();
await next();
});
Or as a more qualified wrapper extension:
public static class SesssionAsyncExtensions
{
/// <summary>
/// Have sessions be asyncronous. This adaptation is needed to force the session provider to use async calls instead of syncronous ones for session.
/// Someone surprisingly for something that seems common, Microsoft didn't make this aspect super nice.
/// </summary>
/// <param name="app">App builder instance.</param>
/// <returns>App builder instance for chaining.</returns>
/// <remarks>
/// From Microsoft Documentation (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0):
/// The default session provider in ASP.NET Core will only load the session record from the underlying IDistributedCache store asynchronously if the
/// ISession.LoadAsync method is explicitly called before calling the TryGetValue, Set or Remove methods.
/// Failure to call LoadAsync first will result in the underlying session record being loaded synchronously,
/// which could potentially impact the ability of an application to scale.
///
/// See also:
/// https://github.com/dotnet/aspnetcore/blob/d2a0cbc093e1e7bb3e38b55cd6043e4e2a0a2e9a/src/Middleware/Session/src/DistributedSession.cs#L268
/// https://github.com/dotnet/AspNetCore.Docs/issues/1840#issuecomment-454182594
/// https://bartwullems.blogspot.com/2019/12/aspnet-core-load-session-state.html
/// </remarks>
public static IApplicationBuilder UseAsyncSession(this IApplicationBuilder app)
{
app.UseSession();
app.Use(async (context, next) =>
{
await context.Session.LoadAsync();
await next();
});
return app;
}
}
It seems you need to implement ISessonStore too (which is actually mentioned in the documentation you quoted), as it's the only one registered in AddSession extension method.
public static IServiceCollection AddSession(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddTransient<ISessionStore, DistributedSessionStore>();
services.AddDataProtection();
return services;
}
ISessionStore (and hence DistributedSessionStore) has a Create (see source) method which returns ISession. Here you need to return your custom implementation.
https://github.com/aspnet/Session/blob/rel/1.1.0/src/Microsoft.AspNetCore.Session/SessionServiceCollectionExtensions.cs#L27-L29
Then you can add before AddSession with
services.AddTransient<ISessionStore, AsyncDistributedSessionStore>();

Categories