[Solved] How to make a IHostingService to send emails on order confirm?


A background (hosted) service is a completely different service, using its own thread to do its job. You can’t have your controller “run” something on that service, you have to tell it what to do, and have it do it.

The Background tasks with hosted services section in the docs shows two different ways a long running background service can work :

  • A timed service can run each time a timer fires and do a periodic job, as long as the application is running
  • A queued service waits for messages in a queue and performs a job when a message arrives

Sending an email fits into the second case. You could use the documentation example almost as-is. You can create an IBackgroundTaskQueue interface that clients like your controller can use to submit jobs to run in the background:

public interface IBackgroundTaskQueue
{
    void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);

    Task<Func<CancellationToken, Task>> DequeueAsync(
        CancellationToken cancellationToken);
}

This interface can be added as a dependency in your container’s constructor.
Assuming the injected service is called myJobQueue, the controller can enqueue a job to run in the background with :


IBackgroundTaskQueue _myJobQueue

public MyController(IBackgroundTaskQueue myJobQueue)
{
    _myJobQueue=myJobQueue;
}


public void ConfirmOrder(...)
{
    ...
    if (!tipo.Equals("MENU"))
    {
        var ordId=int.Parse(orderID);
    
  
  _myJobQueue.QueueBackgroundWorkItem(ct=>EmailHelper.SendRiepilogoAsync(piva,ordId )); 
}

async void should only be used for asynchronous event handlers. That’s not what SendRiepilogo is. async void methods can’t be awaited, they are essentially fire-and-forget methods that may never run, as the application doesn’t know it has to await them. The correct syntax should be :

public static async Task SendRiepilogoAsync(string piva, int idOrdine)
{
...
}

The rest of the documentation example can be used as-is.

Simplifying the service

Instead of a generic queued service that runs any available job, you could create a queue that accepts specific message classes only, only an address and order ID, and have the service do the job of retrieving any data and sending the email. Essentially, SendRiepilogoAsync becomes part of the background service. This allows creating services that could eg batch emails, send several emails concurrently, apply throttling etc.

This would allow reusing expensive resources or perform expensive operations just once, eg create the SmptClient and authenticate before starting to process queue messages

solved How to make a IHostingService to send emails on order confirm?