Back to "Agtergrondverstuur voor e-mails toevoegen"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET

Agtergrondverstuur voor e-mails toevoegen

Wednesday, 07 August 2024

##Inleiding

In mijn vorige post legde ik uit hoe je e-mails kan sturen met FluentEmail en de SMTP-Client. Maar één probleem hieraan is de vertraging in het versturen van emails. SmTP servers zijn meestal traag en kunnen een tijdje duren om emails te versturenM SK2 Dit kan vervelend zijn voor gebruikers en je voelen als een logjam in je applicatie .

Eén manier om dit te vermijden is emails in de achtergrond te sturen. Zo kan de gebruiker doorgaan met het gebruik van de applicatie zonder te hoeven wachten tot de e-mail wordt gestuurd. Dit is een gemeenschappelijk patroon in webapplicaties en kan bereikt worden met behulp van een achtergrondwerk.

Agtergrondopties in ASP.NET Core

In ASP.NET Core heb je twee grote opties ( naast meer geavanceerde opties zoals Hangfire / QuartzM SK3

  • IHostedService - deze optie geeft je een basis levenscyclus-management voor je achtergrondtaakjes. Je kunt de dienst starten en stoppen en het zal in de achtergrond lopenM SK2
  • IHostedLifetime - deze optie geeft je meer controle over de levenscyclus van je achtergrondtaaken. Je kan ook de dienst starten en stoppen en het zal in de achtergrond draaien, maar je hebt meer controle bij het startenM SK2 stoppen , beginnenMSC4 stoppen, etc.

In dit voorbeeld zal I' een eenvoudige IHostedService gebruiken om e-mails in de achtergrond te sturen.

Broncode

De volledige bron hierfür is onder.

Background Email Service
using System.Threading.Tasks.Dataflow;
using Mostlylucid.Email.Models;

namespace Mostlylucid.Email
{
    public class EmailSenderHostedService(EmailService emailService, ILogger<EmailSenderHostedService> logger)
        : IHostedService, IDisposable
    {
        private readonly BufferBlock<BaseEmailModel> _mailMessages = new();
        private Task _sendTask = Task.CompletedTask;
        private CancellationTokenSource cancellationTokenSource = new();

        public async Task SendEmailAsync(BaseEmailModel message)
        {
            await _mailMessages.SendAsync(message);
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            logger.LogInformation("Starting background e-mail delivery");
            // Start the background task
            _sendTask = DeliverAsync(cancellationTokenSource.Token);
            return Task.CompletedTask;
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            logger.LogInformation("Stopping background e-mail delivery");

            // Cancel the token to signal the background task to stop
            await cancellationTokenSource.CancelAsync();

            // Wait until the background task completes or the cancellation token triggers
            await Task.WhenAny(_sendTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }

        private async Task DeliverAsync(CancellationToken token)
        {
            logger.LogInformation("E-mail background delivery started");

            while (!token.IsCancellationRequested)
            {
                BaseEmailModel? message = null;
                try
                {if(_mailMessages.Count == 0) continue;
                    message = await _mailMessages.ReceiveAsync(token);
                    switch (message)
                    {
                        case ContactEmailModel contactEmailModel:
                            await emailService.SendContactEmail(contactEmailModel);
                            break;
                        case CommentEmailModel commentEmailModel:
                            await emailService.SendCommentEmail(commentEmailModel);
                            break;
                    }
                    logger.LogInformation("Email from {SenderEmail} sent", message.SenderEmail);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception exc)
                {
                    logger.LogError(exc, "Couldn't send an e-mail from {SenderEmail}", message?.SenderEmail);
                    await Task.Delay(1000, token); // Delay and respect the cancellation token
                    if (message != null)
                    {
                        await _mailMessages.SendAsync(message, token);
                    }
                }
            }

            logger.LogInformation("E-mail background delivery stopped");
        }

        public void Dispose()
        {
            cancellationTokenSource.Cancel();
            cancellationTokenSource.Dispose();
        }
    }
}
Hier kun je zien dat we het begin van de dienst beheren en een nieuwe BufferBlock opzetten om de e-mails te houden.
public class EmailSenderHostedService(EmailService emailService, ILogger<EmailSenderHostedService> logger)
        : IHostedService, IDisposable
    {
        private readonly BufferBlock<BaseEmailModel> _mailMessages = new();
        private Task _sendTask = Task.CompletedTask;
        private CancellationTokenSource cancellationTokenSource = new();

We hebben ook een nieuwe taak opgezet om de e-mails in de achtergrond te versturen. en een CancellationTokenSource om de taak gracieus te verwerpen als we de dienst willen stoppen.

We starten dan de HostedService met StartAsync en geven de ingangspunt voor andere diensten om een e-mail te sturen.

 public async Task SendEmailAsync(BaseEmailModel message)
        {
            await _mailMessages.SendAsync(message);
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            logger.LogInformation("Starting background e-mail delivery");
            // Start the background task
            _sendTask = DeliverAsync(cancellationTokenSource.Token);
            return Task.CompletedTask;
        }

In onze Opstellingsklas moeten we nu de dienst registreren met het DI-container en de HostedService starten.

       services.AddSingleton<EmailSenderHostedService>();
        services.AddHostedService(provider => provider.GetRequiredService<EmailSenderHostedService>());

Nu kunnen we e-mails in de achtergrond sturen door de SendEmailAsync-methode op de EmailSenderHostedService te roepen. e.g., voor het Kontaktformular doen we dit

            var contactModel = new ContactEmailModel()
            {
                SenderEmail = user.email,
                SenderName =user.name,
                Comment = commentHtml,
            };
            await sender.SendEmailAsync(contactModel);

In de code hierboven voegt dit bericht toe. BufferBlock<BaseEmailModel> _mailMessages en de achtergrond taak pakken het op en sturen de e-mail.

   private async Task DeliverAsync(CancellationToken token)
        {
          ...

            while (!token.IsCancellationRequested)
            {
                BaseEmailModel? message = null;
                try
                {if(_mailMessages.Count == 0) continue;
                    message = await _mailMessages.ReceiveAsync(token);
                    switch (message)
                    {
                        case ContactEmailModel contactEmailModel:
                            await emailService.SendContactEmail(contactEmailModel);
                            break;
                        case CommentEmailModel commentEmailModel:
                            await emailService.SendCommentEmail(commentEmailModel);
                            break;
                    }
                    logger.LogInformation("Email from {SenderEmail} sent", message.SenderEmail);
           ...
            }

            logger.LogInformation("E-mail background delivery stopped");
        }

Dit loopt tot we de dienst stoppen en de BufferBlock blijven monitoren voor nieuwe e-mails om te sturen.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.