1

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 });
    }
NiceToMytyuk
  • 2,262
  • 17
  • 47
  • You broke the dependency injection. The whole point is to inject things and you went around and made methods static and called them directly. Make them non-static and inject an instance and it should work. – nvoigt Oct 08 '20 at 08:21
  • @nvoigt i've tryed by making ConfirmOrder not static but by doing `var helper = new OrdineHelper(); helper.ConfirmOrder()` OrdineHelper() requires taskQueue in it's constructor.. what should i pass in it? – NiceToMytyuk Oct 08 '20 at 08:27
  • 3
    Quite frankly, before you start finding the error here, I suggest you read a tutorial on how dependency injection works in your setting. You will have to roll back your design 2 or three steps and remove all the statics, because that is the opposite of how it works and it's too long to explain it here. – nvoigt Oct 08 '20 at 08:35

0 Answers0