3 votes

Exécuter le service Windows dans un thread séparé et utiliser autofac pour DI

J'essaie de créer un service Windows à long terme, et je dois donc exécuter la classe de travail réelle sur un fil d'exécution distinct, afin d'éviter l'erreur "le service n'a pas répondu en temps voulu" lorsque je fais un clic droit et que je sélectionne démarrer dans le gestionnaire de services Windows.

La classe de travailleur ("NotificationProcess") a toute une série de dépendances et j'utilise Autofac pour les satisfaire.

Je ne sais vraiment pas comment configurer Autofac pour la classe ouvrière. Pour le moment, je reçois des erreurs m'indiquant que le DbContext a été éliminé lorsque je veux l'utiliser dans la méthode "Execute" de la classe worker.

Je pense que je cherche à savoir comment écrire un service Windows et utiliser un nouveau thread pour la classe worker avec des dépendances satisfaites par autofac.

J'ai cherché sur Google et je n'ai pas trouvé d'exemples de ce genre.

Toute suggestion serait la bienvenue.

Voici ce que j'ai pour l'instant...

Programme.cs :

    static class Program
{
    static void Main()
    {
        using (var container = ServiceStarter.CreateAutoFacContainer())
        {
            var service = container.Resolve<NotificationService>();
            if (Environment.UserInteractive)
            {
                service.Debug();
            }
            else
            {
                ServiceBase.Run(container.Resolve<NotificationService>());
            }
        }

La classe Service :

public partial class NotificationService : ServiceBase
{
    private NotificationProcess _app;
    readonly ILifetimeScope _lifetimeScope;

    public NotificationService(ILifetimeScope lifetimeScope)
    {
        _lifetimeScope = lifetimeScope;
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        _app = _lifetimeScope.Resolve<NotificationProcess>();
        _app.Start();
    } 

La classe ouvrière :

    public class NotificationProcess
{
    private Thread _thread;

    private readonly IBankService _bankService;
    private readonly IRateService _rateService;
    private readonly IEmailService _emailService;
    private readonly IRateChangeSubscriberService _rateChangeSubscriberService;
    private readonly IRateChangeNotificationService _rateChangeNotificationService;
    private readonly ILogManager _logManager;

    public NotificationProcess(IBankService bankService, ILogManager logManager, IRateService rateService, IEmailService emailService, 
        IRateChangeSubscriberService rateChangeSubscriberService, IRateChangeNotificationService rateChangeNotificationService)
    {
        _bankService = bankService;
        _rateService = rateService;
        _emailService = emailService;
        _rateChangeSubscriberService = rateChangeSubscriberService;
        _rateChangeNotificationService = rateChangeNotificationService;
        _logManager = logManager;
    }

    public void Start()
    {
        _thread = new Thread(new ThreadStart(Execute));
        _thread.Start();
    }

    public void Execute()
    {
        try
        {
            var rateChangeToNotify = _rateService.GetRateChangesForNotification();

            foreach (var rateChange in rateChangeToNotify)
            {
                 //do whatever business logic.....
            }
        }
    }

4voto

Steven Points 56939

La réponse est en fait simple : utilisez le scoping ! Vous devez faire ce qui suit :

  1. Enregistrez tous les services (tels que DbContext ) qui doit vivre pendant la durée d'une demande ou d'une action avec le style de vie LifetimeScope. Vous aurez généralement une minuterie dans votre service Windows. Chaque "impulsion" peut être considérée comme une demande.
  2. Au début de chaque demande, commencez un champ de vie.
  3. Dans cette portée, résolvez l'objet Root à partir du graphe d'objets et appelez sa méthode.
  4. Disposez de la portée.

Dans votre cas, cela signifie que vous devez modifier votre conception, car NotificationService est résolu une fois et ses dépendances sont réutilisées sur un autre thread. Il s'agit d'un no-no au pays de l'injection de dépendances.

Voici un autre modèle :

// This method is called on a background thread 
// (possibly in a timely manner)
public void Run()
{
    try
    {
        using (var scope = container.BeginLifetimeScope())
        {
            var service = scope.Resolve<NotificationService>();
            service.Execute();
        }
    }
    catch (Exception ex)
    {
        // IMPORTANT: log exception. 
        // Not logging an exception will leave us in the dark.
        // Not catching the exception will kill our service 
        // because we run in a background thread.
    }
}

L'utilisation d'une lunette de visée à vie vous permet d'obtenir un nouveau DbContext pour chaque demande et il vous permettrait même d'exécuter des demandes en parallèle (chaque demande ayant sa propre DbContext ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X