Back to "Assurer votre service IHostedService (ou IHostedLifecycleService) est une seule instance"

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

Assurer votre service IHostedService (ou IHostedLifecycleService) est une seule instance

Thursday, 22 August 2024

Présentation

C'est un petit article stupide parce que j'étais un peu confus sur la façon de s'assurer que mon IHostedService a été un cas unique. Je pensais que c'était un peu plus compliqué qu'en fait. Alors j'ai pensé écrire un petit article à ce sujet. Juste au cas où quelqu'un d'autre serait confus à ce sujet.

Dans le article précédent, nous avons couvert la façon de créer un service d'arrière-plan en utilisant le IHostedService interface pour l'envoi d'emails. Cet article traitera de la façon de s'assurer que votre IHostedService est une seule instance. Cela pourrait être évident pour certains, mais ce n'est pas pour d'autres (et ce n'était pas tout de suite pour moi!).............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

Pourquoi est-ce un problème?

Eh bien, son un problème que la plupart des articles sur ceux-ci couvrent comment utiliser un IHostedService mais ils ne couvrent pas comment s'assurer que le service est une seule instance. Ceci est important car vous ne voulez pas plusieurs instances du service en cours d'exécution en même temps.

Qu'est-ce que je veux dire? Bien dans ASP.NET la façon d'enregistrer un IHostedService ou IHostedlifeCycleService (essentiellement la même avec plus de dérogations pour la gestion du cycle de vie) vous utilisez ce

  services.AddHostedService(EmailSenderHostedService);

Ce que cela fait est d'appeler dans ce code de moteur:

public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
            where THostedService : class, IHostedService
        {
            services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());

            return services;
        }

Ce qui est bien et dandy mais que faire si vous voulez poster un nouveau message directement à ce service de dire un Controller L'action?


public class ContactController(EmailSenderHostedService sender,ILogger<BaseController> logger) ...
{
   [HttpPost]
    [Route("submit")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Submit([Bind(Prefix = "")] ContactViewModel comment)
    {
        ViewBag.Title = "Contact";
        //Only allow HTMX requests
        if(!Request.IsHtmx())
        {
            return RedirectToAction("Index", "Contact");
        }
      
        if (!ModelState.IsValid)
        {
            return PartialView("_ContactForm", comment);
        }

        var commentHtml = commentService.ProcessComment(comment.Comment);
        var contactModel = new ContactEmailModel()
        {
            SenderEmail = string.IsNullOrEmpty(comment.Email) ? "Anonymous" : comment.Email,
            SenderName = string.IsNullOrEmpty(comment.Name) ? "Anonymous" : comment.Name,
            Comment = commentHtml,
        };
        await sender.SendEmailAsync(contactModel);
        return PartialView("_Response",
            new ContactViewModel() { Email = comment.Email, Name = comment.Name, Comment = commentHtml });

        return RedirectToAction("Index", "Home");
    }
   }

Soit vous devez créer une interface qui s'implémente elle-même IHostedService puis appelez la méthode sur cela ou vous devez vous assurer que le service est une seule instance. Ce dernier est le moyen le plus simple de le faire (dépend de votre scénario cependant, pour tester la méthode Interface pourrait être préféré).

Service d'hébergement

Vous noterez ici qu'il enregistre le service comme un IHostedService, ceci est lié à la gestion du cycle de vie de ce service puisque le cadre ASP.NET utilisera cette inscription pour déclencher les événements de ce service (StartAsync et StopAsync pour IHostedService). Voir ci-dessous. IHostedlifeCycleService est juste une version plus détaillée de IHostedService.

  /// <summary>
  /// Defines methods for objects that are managed by the host.
  /// </summary>
  public interface IHostedService
  {
    /// <summary>
    /// Triggered when the application host is ready to start the service.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Start operation.</returns>
    Task StartAsync(CancellationToken cancellationToken);

    /// <summary>
    /// Triggered when the application host is performing a graceful shutdown.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Stop operation.</returns>
    Task StopAsync(CancellationToken cancellationToken);
  }

namespace Microsoft.Extensions.Hosting
{
  /// <summary>
  /// Defines methods that are run before or after
  /// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" /> and
  /// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
  /// </summary>
  public interface IHostedLifecycleService : IHostedService
  {
    /// <summary>
    /// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
    Task StartingAsync(CancellationToken cancellationToken);

    /// <summary>
    /// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
    Task StartedAsync(CancellationToken cancellationToken);

    /// <summary>
    /// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
    Task StoppingAsync(CancellationToken cancellationToken);

    /// <summary>
    /// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the stop process has been aborted.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
    Task StoppedAsync(CancellationToken cancellationToken);
  }
}

Comment s'assurer que votre service IHosted est une instance unique

Approche de l'interface

L'approche Interface pourrait être plus simple selon votre scénario. Ici vous ajouteriez une interface qui hérite de IHostedService puis ajouter une méthode à cette interface que vous pouvez appeler depuis votre contrôleur.

REMARQUE: Vous devez toujours l'ajouter en tant que service hébergé dans ASP.NET pour que le service fonctionne réellement.

    public interface IEmailSenderHostedService : IHostedService, IDisposable
    {
        Task SendEmailAsync(BaseEmailModel message);
    }

Tout ce dont nous avons besoin, c'est de l'enregistrer comme un simpleton, puis de l'utiliser dans notre contrôleur.

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

ASP.NET verra que cela a la bonne interface décorée et utilisera cette inscription pour exécuter le IHostedService.

Méthode d'usine

Un autre pour s'assurer que votre IHostedService est une seule instance est d'utiliser le AddSingleton méthode d'enregistrement de votre service puis passer le IHostedService l'enregistrement en tant que «méthode d'usine». Cela permettra de s'assurer qu'une seule instance de votre service est créée et utilisée tout au long de la durée de vie de l'application.

    1. Le rôle de l'Organisation des Nations Unies dans le domaine de l'éducation, de la science et de la culture usine méthode est juste une façon fantaisiste de dire une méthode qui crée une instance d'un objet.
        services.AddSingleton<EmailSenderHostedService>();
        services.AddHostedService(provider => provider.GetRequiredService<EmailSenderHostedService>());

Donc, comme vous le voyez ici, j'inscris d'abord mon IHostedService (ou IHostedLifeCycleService) comme un simpleton et puis j'utilise le AddHostedService méthode d'enregistrement du service comme méthode d'usine. Cela permettra de s'assurer qu'une seule instance du service est créée et utilisée tout au long de la durée de vie de l'application.

En conclusion

Comme d'habitude, il y a deux façons de peler un chat. L'approche de la méthode d'usine est également un bon moyen de s'assurer que votre service est une seule instance. C'est à vous de décider de l'approche que vous prenez. J'espère que cet article vous aidera à comprendre comment vous assurer que votre IHostedService est une seule instance.

logo

©2024 Scott Galloway