I have the below class in a webapi which adds to the dictionary however everytime the checkstatus method is triggered (using Task.Delay) it says the dictionary is empty. Is this because its running on a different thread? Should I be using a concurrentdictionary instead?
internal class Sms : smsBase
{
private const int CheckFrequencyMilliseconds = 1000;
private Dictionary<CustomerDetails, decimal> _registeredCustomers;
public Sms(ILogger<Sms> logger)
: base(TimeSpan.FromMilliseconds(CheckFrequencyMilliseconds), logger)
{
_registeredCustomers = new Dictionary<CustomerDetails, decimal>();
}
protected override Task CheckStatus()
{
foreach (var rc in _registeredCustomers)
{
//do something
}
return Task.CompletedTask;
}
public Task RegisterCustomer(CustomerDetails customer)
{
_registeredCustomers.Add(customer, 1);
return Task.CompletedTask;
}
}
Base class code below.
public abstract class smsBase : BackgroundService
{
private readonly TimeSpan _tickFrequency;
private readonly ILogger<smsBase> _logger;
/// <inheritdoc />
/// <param name="tickFrequency">Frequency that the service will execute the CheckStatus method.</param>
protected smsBase (TimeSpan tickFrequency,ILogger<smsBase> logger)
{
this._tickFrequency = tickFrequency;
this._logger = logger;
}
/// <inheritdoc />
protected override sealed async Task ExecuteAsync(CancellationToken stoppingToken)
{
this._logger.LogInformation("Service is starting.");
while (!stoppingToken.IsCancellationRequested)
{
ConfiguredTaskAwaitable configuredTaskAwaitable;
try
{
configuredTaskAwaitable = this.CheckStatus().ConfigureAwait(false);
await configuredTaskAwaitable;
}
catch (Exception ex)
{
this._logger.LogError(ex, "An exception was thrown whilst checking registered strategies.");
throw;
}
configuredTaskAwaitable = Task.Delay(this._tickFrequency, stoppingToken).ConfigureAwait(false);
await configuredTaskAwaitable;
}
this._logger.LogInformation("Service is stopping.");
protected abstract Task CheckStatus();
}
And below is the startup cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new Info() { Title = "Customers.WebApi", Version = "v1" });
})
.AddMvc(options => {
options.Filters.Add(new ExceptionFilter());
options.Filters.Add(new ProducesAttribute("application/json"));
})
.AddJsonOptions(options => {
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddHostedService<Sms>();
services.AddSingleton<ISms, Sms>();
}
There are many points missing from your question, but I believe I can derive the problem from just these 2 lines
services.AddHostedService<Sms>();
services.AddSingleton<ISms, Sms>();
The AddSingleton
line will create a single instance of Sms
for your application, and I suspect you're injecting ISms
into a controller somewhere and calling RegisterCustomer
.
The AddHostedService
line will create a second instance of Sms, and use it as the hosted service.
The solution is to split these 2 things apart, and have something like ICustomerRepository
which is shared by both the hosted service and the controllers.
Note that if I'm well off the mark here, it would help to edit your question with the details of ISms
, including how it is used, and where you call RegisterCustomer
.