C# MediatR error: Register your handlers with the container - c#

Every time that i try to call Send from MediatR to any Query/Command that i have, it returns this Exception:
System.InvalidOperationException: 'Error constructing handler for request of type MediatR.IRequestHandler2[CQRSHost.Recursos.Queries.GetTodosProdutosQuery,System.Collections.Generic.IEnumerable1[CQRSHost.Models.Produto]]. Register your handlers with the container. See the samples in GitHub for examples.'
Inner Exception:
InvalidOperationException: Cannot resolve 'MediatR.IRequestHandler2[CQRSHost.Recursos.Queries.GetTodosProdutosQuery,System.Collections.Generic.IEnumerable1[CQRSHost.Models.Produto]]' from root provider because it requires scoped service 'CQRSHost.Context.AppDbContext'.
But i have the AppDbContext in my DI container:
public static IHostBuilder CreateHostBuilder(string[] args)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
return Host.CreateDefaultBuilder(args)
.UseSerilog()
.UseEnvironment("Development")
.ConfigureHostConfiguration(hostConfig =>
{
hostConfig.SetBasePath(Directory.GetCurrentDirectory());
hostConfig.AddEnvironmentVariables("DSO_");
})
.ConfigureServices((context, services) =>
{
services.AddSingleton(ConfigureLogger());
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
"Server=localhost;Database=newdatabase;User Id=sa;Password=P#ssw0rd!##$%;",
b => b.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName)));
services.AddHostedService<NewService>();
//services.AddMediatR(Assembly.GetExecutingAssembly());
//services.AddMediatR(typeof(GetTodosProdutosQuery).GetTypeInfo().Assembly);
services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies());
});
}
Here is the service that i use to call the query:
public class NewService : IHostedService
{
private readonly ILogger _logger;
private readonly IMediator _mediator;
public NewService(ILogger logger, IMediator mediator)
{
_logger = logger;
_mediator = mediator;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var command = new GetTodosProdutosQuery();
var response = await _mediator.Send(command);
_logger.Information($"First Name: {response.First()?.Nome}");
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
And here is what my project looks like:
ProjectImage
The commented lines is what i've already tryied to solve.
What am i doing wrong?

The exception "Register your handlers with the container." is misleading. The real error is described in the inner exception:
Cannot resolve 'MediatR.IRequestHandler<GetTodosProdutosQuery, IEnumerable>' from root provider because it requires scoped service 'CQRSHost.Context.AppDbContext'.
This happens because you inject the IMediator into a singleton consumer NewService. The Mediator implementation depends on a IServiceProvider but as NewService is singleton, it is resolved from the root container, and so will all its dependencies recursively. This means that once Mediator starts resolving from its IServiceProvider, it also resolves from the root container. And scoped services can't be resolved from the root container, because that would lead to bugs, because that scoped service would be cached for the lifetime of the root container, and reused for the lifetime of the root container - which means indefinitely.
The solution is to inject an IServiceScope into NewService create a scope from within its StartAsync and resolve the IMediator from there:
public class NewService : IHostedService
{
private readonly IServiceProvider _container;
public NewService(IServiceProvider container)
{
_container = container;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
await using (var scope = _container.CreateScope())
{
var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var command = new GetTodosProdutosQuery();
var response = await mediator.Send(command);
logger.Information($"First Name: {response.First()?.Nome}");
}
}
...
}
Another, perhaps more convenient option would be to ensure that the mediator always resolves from a new scope. This can be achieved using the following code:
public record ScopedSender<TSender>(IServiceProvider Provider)
: ISender where TSender : ISender
{
public Task<TResponse> Send<TResponse>(
IRequest<TResponse> request, CancellationToken ct)
{
async using (var scope = Provider.CreateScope());
var sender = scope.ServiceProvider.GetRequiredService<TSender>();
return await sender.Send(request, ct);
}
public Task<object?> Send(object request, CancellationToken ct)
{
async using (var scope = Provider.CreateScope());
var sender = scope.ServiceProvider.GetRequiredService<TSender>();
return await sender.Send(request, ct);
}
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
IStreamRequest<TResponse> request, CancellationToken ct)
{
async using (var scope = Provider.CreateScope());
var sender = scope.ServiceProvider.GetRequiredService<TSender>();
return await sender.CreateStream(request, ct);
}
public IAsyncEnumerable<object?> CreateStream(object request, CancellationToken ct)
{
async using (var scope = Provider.CreateScope());
var sender = scope.ServiceProvider.GetRequiredService<TSender>();
return await sender.CreateStream(request, ct);
}
}
Now configure this as follows:
services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies());
services.AddTransient<Mediator>();
services.AddSingleton<ISender, ScopedSender<Mediator>>();
Now you can safely inject your ISender into yout NewService without having to apply scoping:
public class NewService : IHostedService
{
private readonly ILogger _logger;
private readonly IMediator _sender;
public NewService(ILogger logger, ISender sender)
{
_logger = logger;
_sender = sender;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var command = new GetTodosProdutosQuery();
var response = await _sender.Send(command);
_logger.Information($"First Name: {response.First()?.Nome}");
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

I was having the same problem, I solved it here by changing AddScoped to AddSingleton and adding builder.Services.AddTransient();

Related

Autofac Issue: Cannot resolve parameter of constructor 'Void .ctor

My .Net 7 application has the following issue:
Autofac.Core.DependencyResolutionException: An exception was thrown while activating MyApp.Modules.MyModule.Application.MyModule.UpdateCommand.UpdateCommandHandler.
---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'MyApp.Modules.MyModule.Infrastructure.Configuration.AllConstructorFinder' on type 'MyApp.Modules.MyModule.Application.MyModule.UpdateCommand.UpdateCommandHandler' can be invoked with the available services and parameters:
Cannot resolve parameter 'MyApp.Modules.MyModule.Application.Contracts.IMyModule myModule' of constructor 'Void .ctor(Serilog.ILogger, MyApp.Modules.MyModule.Application.Contracts.IMyModule)'.
UpdateCommandHandler.cs (where the issue is occurring)
public class UpdateCommandHandler: ICommandHandler<UpdateCommand>
{
private readonly IMyModule _myModule;
private readonly ILogger _logger;
public UpdateCommandHandler(ILogger logger, IMyModule myModule)
{
_myModule = myModule;
_logger = logger;
}
public async Task<Unit> Handle(UpdateCommand request, CancellationToken cancellationToken)
{
var foo = await _myModule.ExecuteQueryAsync(new SampleQuery());
return Unit.Value;
}
}
Program.cs
...
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(b => b.RegisterModule(new AutofacModules()));
...
I looked at similar issues before posting, such as this, but I do believe I appropriately registered IMyModule in Autofac as MyModule in the following.
AutofacModules.cs
public class AutofacModules: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<MyModule>().As<IMyModule>().InstancePerLifetimeScope();
}
}
IMyModule.cs
public interface IMyModule
{
Task ExecuteCommandAsync(ICommand command);
Task<TResult> ExecuteQueryAsync<TResult>(IQuery<TResult> query);
}
MyModule.cs
public class MyModule: IMyModule
{
public async Task ExecuteCommandAsync(ICommand command)
{
await CommandsExecutor.Execute(command);
}
public Task<TResult> ExecuteQueryAsync<TResult>(IQuery<TResult> query)
{
var scope = MyCompositionRoot.BeginLifetimeScope();
var mediator = scope.Resolve<IMediator>();
return mediator.Send(query);
}
}
AllConstructorFinder.cs
internal class AllConstructorFinder : IConstructorFinder
{
private static readonly ConcurrentDictionary<Type, ConstructorInfo[]> Cache = new();
public ConstructorInfo[] FindConstructors(Type targetType)
{
var result = Cache.GetOrAdd(targetType, t => t.GetTypeInfo().DeclaredConstructors.ToArray());
return result.Length > 0 ? result : throw new NoConstructorsFoundException(targetType);
}
}
In my Program.cs, I had registered MyModule, but, as I have multiple modules with their own containers, I didn't register it in the module's own composition root. By adding the following line, I'm able to include MyModule as a constructor parameter.
MyModuleStartup.cs
...
var containerBuilder = new ContainerBuilder();
...
/* NEW LINE */
containerBuilder.RegisterType<CventModule>().As<ICventModule>().InstancePerLifetimeScope();
...
So lesson here is make sure the component you are using is registered to Autofac root container your module is running directly in. Thanks to #Travis Illig for the troubleshooting link which helped me immensely.

Multiple Background Services in .NET Core

Create Multiple Background tasks in .NET 6 which are independent to each other in functionality aspect and runs parallelly / simultaneously depending on the scheduled timings.
With Worker class template i was able to create multiple Hosted / Background services and they are running as expected.
But services.AddHostedService<Worker>(); will be taken as Singleton class & we need to resolve scoped dependency in order to make the service as scoped, followed the same from scoped service document.
As per the example in the above link, sample code looks like this,
Interface of Scoped service
namespace App.ScopedService;
public interface IScopedProcessingService
{
Task DoWorkAsync(CancellationToken stoppingToken);
}
and the default implementation of the interface
namespace App.ScopedService;
public class DefaultScopedProcessingService : IScopedProcessingService
{
private int _executionCount;
private readonly ILogger<DefaultScopedProcessingService> _logger;
public DefaultScopedProcessingService(
ILogger<DefaultScopedProcessingService> logger) =>
_logger = logger;
public async Task DoWorkAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
++ _executionCount;
_logger.LogInformation(
"{ServiceName} working, execution count: {Count}",
nameof(DefaultScopedProcessingService),
_executionCount);
await Task.Delay(10_000, stoppingToken);
}
}
}
And this is the Background service implementation
namespace App.ScopedService;
public sealed class ScopedBackgroundService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ScopedBackgroundService> _logger;
public ScopedBackgroundService(
IServiceProvider serviceProvider,
ILogger<ScopedBackgroundService> logger) =>
(_serviceProvider, _logger) = (serviceProvider, logger);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
$"{nameof(ScopedBackgroundService)} is running.");
await DoWorkAsync(stoppingToken);
}
private async Task DoWorkAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
$"{nameof(ScopedBackgroundService)} is working.");
using (IServiceScope scope = _serviceProvider.CreateScope())
{
IScopedProcessingService scopedProcessingService =
scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWorkAsync(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
$"{nameof(ScopedBackgroundService)} is stopping.");
await base.StopAsync(stoppingToken);
}
}
and Program.cs would be something like follows
using App.ScopedService;
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<ScopedBackgroundService>();
services.AddScoped<IScopedProcessingService, DefaultScopedProcessingService>();
})
.Build();
await host.RunAsync();
If i have another Background service, then which all code i can re-use and how do i resolve scoped service ?
You can create multiple services implementing IHostedService interface and then register them just like that:
builder.Services.AddHostedService<HostedServiceA>();
builder.Services.AddHostedService<HostedServiceB>();
builder.Services.AddHostedService<HostedServiceC>();
If you want to access scoped service inside the singleton the easiest way is to inject IServiceScopeFactory in constructor:
public HostedServiceA(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
}
and then you can access scoped services inside the methods by calling:
using var scope = _serviceScopeFactory.CreateScope();
var someScopedService = scope.ServiceProvider.GetService<ISomeScopedService>();
var someOtherScopedService = scope.ServiceProvider.GetService<ISomeOtherScopedService>();

Some services are not able to be constructed Error while validating the service descriptor

I have a ASP.NET Core 5.0 MVC solution,
public abstract class HostedService : IHostedService, IDisposable
{
private Task _currentTask;
private readonly CancellationTokenSource _cancellationTokenSource = new
CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken token);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
_currentTask = ExecuteAsync(cancellationToken);
return _currentTask.IsCompleted ? _currentTask : Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
if (_currentTask == null) return;
try
{
_cancellationTokenSource.Cancel();
}
finally
{
await Task.WhenAny(_currentTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
public virtual void Dispose()
{
_cancellationTokenSource.Cancel();
}
}
Gets the exchange rates from URL
public class ExchangeSyncManager : HostedService
{
private readonly CurrencyServices _currencyServices;
private readonly ExchangeRateServices _exchangeRateServices;
public ExchangeSyncManager(CurrencyServices currencyServices, ExchangeRateServices
exchangeRateServices)
{
_currencyServices = currencyServices;
_exchangeRateServices = exchangeRateServices;
}
protected override async Task ExecuteAsync(CancellationToken token)
{
// işlem iptal edilmemişse…
if (!token.IsCancellationRequested)
{
var url = "http://www.tcmb.gov.tr/kurlar/today.xml";
XmlDocument xmlVerisi = new XmlDocument();
List<ExchangeRate> list = new List<ExchangeRate>();
xmlVerisi.Load(url);
foreach (var currency in _currencyServices.GetCurrencies())
{
var format = string.Format("Tarih_Date/Currency[#Kod='{0}']/ForexSelling", currency.Name);
var selectAndReplace = xmlVerisi.SelectSingleNode(format).InnerText.Replace('.', ',');
decimal value = Convert.ToDecimal(selectAndReplace);
list.Add(new ExchangeRate
{
Date = DateTime.Now,
Value = value,
CurrencyId = currency.Id
});
}
_exchangeRateServices.AddRange(list);
await Task.Delay(TimeSpan.FromDays(1), token);
}
}
}
And in startup :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
//other services here
......................................................
.....................................................
services.AddScoped<CurrencyServices, CurrencyServices>();
services.AddScoped<ExchangeRateServices>();
services.AddHostedService<ExchangeSyncManager>();
}
Still I am getting this error :
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: IPMMS.Business.Managers.ExchangeSyncManager': Cannot consume scoped service 'IPMMS.Business.Services.CurrencyServices' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.)'
What is wrong ?
You cannot inject a scoped services inside a singleton. They are bound to HTTP requests.
A HostedService's lifetime is singleton.
However you can use the IServiceProvider to create a scope and retrieve an instance of your scoped service.
You will find how to fix your issue here :
https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service

Unable to Consume Scoped Service via MediatR

I have an ASP.NET Core application running .NET 5 and C# 9. This also runs a Discord bot in the background. My ConfigureServices() method in Startup.cs looks like this.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var client = new DiscordSocketClient(new DiscordSocketConfig
{
AlwaysDownloadUsers = true,
MessageCacheSize = 10000,
GatewayIntents = GatewayIntents.Guilds | GatewayIntents.GuildMessages |
GatewayIntents.GuildMessageReactions | GatewayIntents.GuildPresences,
LogLevel = LogSeverity.Info
});
var commandService = new CommandService(new CommandServiceConfig
{
LogLevel = LogSeverity.Debug,
DefaultRunMode = RunMode.Sync,
CaseSensitiveCommands = false,
IgnoreExtraArgs = false,
});
services
.AddMediatR(Assembly.GetEntryAssembly())
.AddHostedService<StartupService>()
.AddHostedService<DiscordListener>()
.AddScoped<ITestService, TestService>()
.AddSingleton(client)
.AddSingleton(provider =>
{
commandService.AddModulesAsync(Assembly.GetEntryAssembly(), provider);
return commandService;
})
.AddSingleton(Configuration);
}
As you can see, I have added ITestService and TestService as a scoped service.
public class TestService : ITestService
{
public async Task<string> GetString()
{
await Task.Delay(1);
return "hey";
}
}
public interface ITestService
{
Task<string> GetString();
}
I then inject this service into my command module.
public class TestModule : ModuleBase<SocketCommandContext>
{
private readonly ITestService _testService;
public TestModule(ITestService testService)
{
_testService = testService;
}
[Command("ping")]
public async Task Ping()
{
var str = await _testService.GetString();
await ReplyAsync(str);
}
}
However, the application does not respond to the ping command. In fact, my handler for receiving messages is not hit at all (I have checked via breakpoint). This is the hosted services that listens for events and publishes the relevant MediatR notifications.
public partial class DiscordListener : IHostedService
{
private readonly DiscordSocketClient _client;
private readonly IServiceScopeFactory _serviceScopeFactory;
public DiscordListener(
DiscordSocketClient client,
IServiceScopeFactory serviceScopeFactory)
{
_client = client;
_serviceScopeFactory = serviceScopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_client.MessageReceived += MessageReceived;
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_client.MessageReceived -= MessageReceived;
return Task.CompletedTask;
}
// Creating our own scope here
private async Task MessageReceived(SocketMessage message)
{
using var scope = _serviceScopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediator.Publish(new MessageReceivedNotification(message));
}
}
And this is the notification handler that handles the notification.
public class CommandListener : INotificationHandler<MessageReceivedNotification>
{
private readonly IConfiguration _configuration;
private readonly DiscordSocketClient _client;
private readonly CommandService _commandService;
private readonly IServiceProvider _serviceProvider;
public CommandListener(
IConfiguration configuration,
DiscordSocketClient client,
CommandService commandService,
IServiceProvider serviceProvider)
{
_configuration = configuration;
_client = client;
_commandService = commandService;
_serviceProvider = serviceProvider;
}
public async Task Handle(MessageReceivedNotification notification, CancellationToken cancellationToken)
{
if (!(notification.Message is SocketUserMessage message)
|| !(message.Author is IGuildUser user)
|| user.IsBot)
{
return;
}
var argPos = 0;
var prefix = _configuration["Prefix"];
if (message.HasStringPrefix(prefix, ref argPos))
{
var context = new SocketCommandContext(_client, message);
using var scope = _serviceProvider.CreateScope();
await _commandService.ExecuteAsync(context, argPos, scope.ServiceProvider);
}
}
}
Just to clarify, the breakpoint at _client.MessageReceoved += ... is not hit. If I change the ITestService and TestService implementation to a Singleton, then the handler is hit and the command works as expected. Any idea on what I'm doing wrong?
Here is the GitHub repo to the project if you want to see the full code. It is not too large.
This a typical problem when mixing Singleton and scoped services. If you end up with situation a singleton is resolving a scoped service it is not allowed.
From docs here
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
Do not resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests. It's fine to:
Resolve a singleton service from a scoped or transient service.
Resolve a scoped service from another scoped or transient service.
By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. For more information, see Scope validation.
Also more discussion on https://dotnetcoretutorials.com/2018/03/20/cannot-consume-scoped-service-from-singleton-a-lesson-in-asp-net-core-di-scopes/amp/

How can I create a BackGround service that runs a function every given period of time ? Using C# (asp.net core 3.1.1)

I'm trying to make call to a function every specified interval of time, for that m using Background service, here is what I have done:
Here is the Alerting controller where I have the function:
public class AlertingController : ControllerBase
{
private readonly DatabaseContext _context;
private readonly IMapper _mapper;
public AlertingController(DatabaseContext context, IMapper mapper)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public AlertingController()
{
}
//function that adds in the DB
public async Task<AlertingResponse> GetAlertingToDB()
{
AlertingResponse dataGet;
using (var httpClient = new HttpClient())
{
using (var response = await httpClient
.GetAsync(MetricApiLink))
{
string apiResponse = await response.Content.ReadAsStringAsync();
dataGet = JsonConvert.DeserializeObject<AlertingResponse>(apiResponse);
}
}
if (dataGet.data.alerts != null || dataGet.data.alerts.Count > 0)
{
foreach (var alert in dataGet.data.alerts)
{
CreateAlertQuery QueryAlert = new CreateAlertQuery();
QueryAlert.Name = alert.labels.alertname;
QueryAlert.Instance = alert.labels.instance;
QueryAlert.Serverity = alert.labels.severity;
QueryAlert.Summary = alert.annotations.summary;
QueryAlert.State = alert.state;
QueryAlert.ActiveAt = alert.activeAt;
var _Alert = _mapper.Map<AlertingDataModel>(QueryAlert);
_context.Alertings.Add(_Alert);
await _context.SaveChangesAsync();
}
}
return null;
}
}
I have tested the method with a HTTPGET request, it works fine, add the alerts into my database:
I have created a scooped service where I called the function GetAlertingToDB():
internal interface IScopedAlertingService
{
Task DoWork(CancellationToken stoppingToken);
}
public class ScopedAlertingService : IScopedAlertingService
{
private int executionCount = 0;
private readonly ILogger _logger;
public ScopedAlertingService(ILogger<ScopedAlertingService> logger)
{
_logger = logger;
}
public async Task DoWork(CancellationToken stoppingToken)
{
AlertingController _AlertingToDB = new AlertingController();
while (!stoppingToken.IsCancellationRequested)
{
executionCount++;
_logger.LogInformation(
"Scoped Processing Service is working. Count: {Count}", executionCount);
await _AlertingToDB.GetAlertingToDB();
await Task.Delay(10000, stoppingToken);
}
}
}
I have also created the Class that will consume my service, and will run in the BackGround:
public class ConsumeScopedServiceHostedService : BackgroundService
{
private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service running.");
await DoWork(stoppingToken);
}
private async Task DoWork(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedAlertingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is stopping.");
await Task.CompletedTask;
}
}
I injected the dependencies on the Startup Class and added the hosted service:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedAlertingService, ScopedAlertingService>();
The functions are working just fine untill a call the GetAlertingToDB() function and it doesn't work.
Any help would be great, thanks everyone :)
Personally I would rearrange your solution so that your background service doesn't need to create a Controller. Instead the controller, if you still need it at all, should call into your ScopedAlertingService where the work is performed once. Your background service can simply loop forever, with an await Task.Delay().
public class ScopedAlertingService : IScopedAlertingService
{
public async Task DoWork(CancellationToken stoppingToken)
{
// move contents of your AlertingController.GetAlertingToDB here
}
}
public class ConsumeScopedServiceHostedService : BackgroundService
{
private readonly IServiceProvider _services;
public ConsumeScopedServiceHostedService(IServiceProvider services)
{
_services = services;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(10000, stoppingToken);
using (var scope = _services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedAlertingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
}
}
Hangfire RecurringJob would be an option for you case. you can check it here https://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html.
The benefit of using it is: you have a dashboard to check when the task will be fired and the result of the task.
There are several options for doing this.
Please read the following link from the Microsoft Documentation which has several examples on how to do this in .NET Core and ASP.NET Core:
Worker Service In NET Core
It is called Worker Services.
You basically implement two interfaces: IHostedService, IDisposable
Then you register your service inside you Startup class in your ConfigureServices method like this:
services.AddHostedService<MyCoolWorkerServiceClass>();
For a Complete Example
One last sugestion. The example uses System.Threading.Timer... But I think it is better to use a System.Timers.Timer with AutoReset = false.
The reason is to avoid overlapping runs of your service. Once a run is done then you start your timer again.
But then again it all depends on what you want to achieve.

Categories

Resources