I'm trying to use IHostingService in my c# WebApi 2 project based on .NET Core.
The IHostingService should send emails via SMPT on each confirmed order via Queue background tasks, i was following the Microsoft docs and the answer of a user which suggested this way in another question, the issue is that when i'm trying to run the _taskQueue i get the following error:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
While i can't get on how i must correctly initiate the IBackgroundTaskQueue to run the task correctly without null ref error...
My code looks like this:
BackgroundTaskQueue.cs:
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
QueueHostedService:
public class QueuedHostedService : BackgroundService
{
private readonly ILogger<QueuedHostedService> _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILogger<QueuedHostedService> logger)
{
TaskQueue = taskQueue;
_logger = logger;
}
public IBackgroundTaskQueue TaskQueue { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
$"Queued Hosted Service is running.{Environment.NewLine}");
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem =
await TaskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error occurred executing {WorkItem}.", nameof(workItem));
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Queued Hosted Service is stopping.");
await base.StopAsync(stoppingToken);
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<OrdineHelper>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
And here is the OrdineHelper.cs
class where i have couple of static functions in which there is the ConfirmOrder function in which i must run the EmailHelper.SendRiepilogoAsync
which returns a Task as QueueBackgroundWorkItem
public class OrdineHelper
{
private static IBackgroundTaskQueue _taskQueue;
public OrdineHelper(IBackgroundTaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
... // LOTS OF STATIC FUNCTIONS
public static void ConfirmOrder(string piva, string orderID, double importo = 0, string transazione = "", string paymentID = "", string tipo = "MENU")
{
string connectionString = getConnectionString(piva);
using var connection = new MySqlConnection(connectionString);
string query_menu = "XXX;";
string query_pagamenti = "XXX";
using var cmd = new MySqlCommand(query_pagamenti, connection);
connection.Open();
...
cmd.ExecuteNonQuery();
if (!tipo.Equals("MENU"))
{
_taskQueue.QueueBackgroundWorkItem(ct => EmailHelper.SendRiepilogoAsync(piva, int.Parse(orderID))); // HERE I MUST RUN QueueBackgroundWorkItem but i get null ref error
}
}
}
The ConfirmOrder is called in other static functions like this:
public static IActionResult InsertOrdineTakeaway(string piva, string idNegozio, RiepilogoTakeaway ordine, HttpResponse Response)
{
...
if (pagamento.type.Equals("CO"))
{
ConfirmOrder(piva, idOrdine, 0, "", pagamento.id.ToString(), ordine.tipo.ToUpper());
}
Response.Cookies.Append("IDORDER", JsonConvert.SerializeObject(new { idOrdine, piva, payment = pagamento.type }), option);
return new OkObjectResult(new { idOrdine });
}