NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Wednesday, 07 August 2024
//Less than a minute
##Introduction
Dans mon post précédent, j'ai expliqué comment envoyer des courriels en utilisant FluentEmail et le client SMTP. Cependant, un problème avec cela est le retard dans l'envoi des courriels. Les serveurs SMTP ont tendance à être lents et peuvent prendre un certain temps pour envoyer des courriels. Cela peut être ennuyeux pour les utilisateurs et se sentir comme un logjam dans votre application.
Une façon de contourner cela est d'envoyer des courriels en arrière-plan. De cette façon, l'utilisateur peut continuer à utiliser l'application sans avoir à attendre l'envoi de l'e-mail. Il s'agit d'un modèle commun dans les applications Web et peut être réalisé à l'aide d'un travail d'arrière-plan.
Dans ASP.NET Core, vous avez deux options principales (en plus d'options plus avancées comme Hangfire / Quartz)
Dans cet exemple, je vais utiliser un simple IHostedService pour envoyer des courriels en arrière-plan.
La source complète pour cela est ci-dessous.
namespace Mostlylucid.Email
{
public class EmailSenderHostedService(EmailService emailService, ILogger
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();
}
}
}
</details>
Ici vous pouvez voir que nous gérons le démarrage du service et la configuration d'un nouveau BufferBlock pour tenir les emails.
```csharp
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();
Nous avons également mis en place une nouvelle tâche pour livrer les e-mails en arrière-plan. et une AnnulationTokenSource pour annuler gracieusement la tâche lorsque nous voulons arrêter le service.
Nous commençons ensuite le service hébergé avec StartAsync et fournissons le point d'entrée pour d'autres services pour envoyer un e-mail.
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;
}
Dans notre classe de configuration, nous devons maintenant enregistrer le service avec le conteneur DI et démarrer le service hébergé
services.AddSingleton<EmailSenderHostedService>();
services.AddHostedService(provider => provider.GetRequiredService<EmailSenderHostedService>());
Maintenant, nous pouvons envoyer des courriels en arrière-plan en appelant la méthode SendEmailAsync sur le service EmailSenderHostedService. Par exemple, pour le formulaire de contact, nous le faisons.
var contactModel = new ContactEmailModel()
{
SenderEmail = user.email,
SenderName =user.name,
Comment = commentHtml,
};
await sender.SendEmailAsync(contactModel);
Dans le code ci-dessus, ceci ajoute ce message à notre BufferBlock<BaseEmailModel>
_mailMessages et la tâche d'arrière-plan vont la récupérer et envoyer l'email.
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");
}
Cela sera alors en boucle jusqu'à ce que nous arrêtions le service et continuons à surveiller le BufferBlock pour de nouveaux courriels à envoyer.