web-dev-qa-db-fra.com

Hébergement de base ASP.NET en tant que service Windows

Comme je le reçois dans RC2, il existe un support pour l'hébergement d'applications dans Windows Services. J'ai essayé de le tester sur un projet simple API Web (à l'aide de .NET Framework 4.6.1).

Voici mon code Program.cs:

using System;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;

namespace WebApplication4
{
  public class Program : ServiceBase
  {
    public static void Main(string[] args)
    {
      if (args.Contains("--windows-service"))
      {
        Run(new Program());
        return;
      }

      var program = new Program();
      program.OnStart(null);
      Console.ReadLine();
      program.OnStop();
    }

    protected override void OnStart(string[] args)
    {
      var Host = new WebHostBuilder()
      .UseKestrel()
      .UseContentRoot(Directory.GetCurrentDirectory())      
      .UseStartup<Startup>()
      .Build();

      Host.RunAsService();
    }

    protected override void OnStop() {}
  }
}

Tous les autres éléments sont essentiellement issus du modèle .NET Core (bien que j'ai changé d'infrastructure en net461 et que j'ai ajouté des dépendances dans project.json). 

Après l'avoir publié avec dotnet publish et créé le service Windows avec sc create, je peux démarrer mon service avec succès, mais je ne peux accéder à aucun de mes contrôleurs (les ports ne sont pas en liste). Je suppose que je fais quelque chose de mal. 

La principale question est donc de savoir comment créer une API Web auto-hébergée et l’exécuter en tant que service Windows. Toutes les solutions trouvées ne fonctionnent pas après la mise à jour de RC2. 

26
Maria P

Vous disposez de plusieurs options: utiliser la classe WebHostService de Microsoft, hériter de WebHostService ou écrire la vôtre. La raison en est que, grâce à l'implémentation de Microsoft, nous ne pouvons pas écrire une méthode d'extension générique avec un paramètre de type héritant de WebHostService car cette classe ne contient pas de constructeur sans paramètre et nous ne pouvons pas accéder au localisateur de service.

Utilisation de WebHostService

Dans cet exemple, je vais créer une application console qui utilise Microsoft.DotNet.Web.targets, génère un fichier .exe et fonctionne comme une application MVC (vues, contrôleurs, etc.). Je suppose que la création de l'application console, la modification des cibles dans .xproj et celle de project.json pour disposer d'options de publication appropriées, ainsi que la copie des vues, des contrôleurs et de la racine Web à partir du modèle d'application Web .NET Core standard, sont triviales. 

Maintenant l'essentiel:

  1. Obtenez this package et assurez-vous que votre infrastructure dans le fichier project.json est net451 (ou plus récente).

  2. Assurez-vous que la racine de contenu du point d'entrée est correctement définie dans le répertoire de publication du fichier .exe de l'application et que la méthode d'extension RunAsService () est appelée. Par exemple:

    public static void Main(string[] args)
    {
        var exePath= System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
        var directoryPath = Path.GetDirectoryName(exePath);
    
        var Host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(directoryPath)
            .UseStartup<Startup>()
            .Build();
    
        if (Debugger.IsAttached || args.Contains("--debug"))
        {
            Host.Run();
        }
        else
        {
            Host.RunAsService();
        }
    }
    

Le fichier .exe peut maintenant être facilement installé à l’aide de la commande suivante

    sc create MyService binPath = "Full\Path\To\The\Console\file.exe"

Une fois le service démarré, l'application Web est hébergée et réussit à trouver et à rendre ses vues.

Hériter WebHostService

L'un des principaux avantages de cette approche est que cela nous permet de remplacer les méthodes OnStopping, OnStarting et OnStarted.

Supposons que ce qui suit est notre classe personnalisée qui hérite de WebHostService. 

internal class CustomWebHostService : WebHostService
{
    public CustomWebHostService(IWebHost Host) : base(Host)
    {
    }

    protected override void OnStarting(string[] args)
    {
        // Log
        base.OnStarting(args);
    }

    protected override void OnStarted()
    {
        // More log
        base.OnStarted();
    }

    protected override void OnStopping()
    {
        // Even more log
        base.OnStopping();
    }
}

En suivant les étapes ci-dessus, nous devons écrire notre propre méthode d'extension qui exécute l'hôte à l'aide de notre classe personnalisée:

public static class CustomWebHostWindowsServiceExtensions
{
    public static void RunAsCustomService(this IWebHost Host)
    {
        var webHostService = new CustomWebHostService(Host);
        ServiceBase.Run(webHostService);
    }
}

La seule ligne qui reste à modifier de l'exemple précédent du point d'entrée est l'instruction else à la fin, elle doit appeler la méthode d'extension appropriée.

Host.RunAsCustomService();

Enfin, installez le service en suivant les mêmes étapes que ci-dessus.

sc create MyService binPath = "Full\Path\To\The\Console\file.exe"
47
Ivan Prodanov

S'il est correct de ne fonctionner que sur .NET Framework complet, les réponses précédentes sont suffisantes (hériter de ServiceBase ou utiliser Microsoft WebHostService).

Si, toutefois, vous voulez/ne voulez exécuter que .NET Core uniquement (par exemple, sur un serveur Windows nano, un binaire unique fonctionnant sous linux en tant qu'application normale et en tant que service sous Windows, ou devant être exécuté côte à côte avec des applications sur une ancienne version du .NET Framework afin que vous ne puissiez pas mettre à jour le framework système), vous devez vous-même appeler les API Windows appropriées via P/Invokes. Comme je devais le faire, j'ai créé une bibliothèque qui fait exactement cela .

Il existe un échantillon disponible qui est également capable de s'installer lui-même lorsqu'il est exécuté avec des privilèges d'administrateur (par exemple, dotnet MyService.dll --register-as-service).

8
Martin Ullrich

Vous devez référencer Microsoft.AspNetCore.Hosting et Microsoft.AspNetCore.Hosting.WindowsServices et utiliser ce code dans votre classe de démarrage pour le rendre exécutable en tant que service Windows:

public static void Main(string[] args)
{
    var Host = new WebHostBuilder()
                .UseIISIntegration()
                .UseKestrel()
                .UseContentRoot(@"Path\To\Content\Root")
                .UseStartup<Startup>()
                .Build();

    if (Debugger.IsAttached || args.Contains("--debug"))
    {
        Host.Run();
    }
    else
    {
        Host.RunAsService();
    }
}

Ensuite, vous devez ajouter une méthode d'extension pour IWebHost.RunAsService afin de l'envelopper avec votre classe WebHostService personnalisée avec les gestionnaires d'événements OnStopping et OnStarting:

public static class MyWebHostServiceServiceExtensions
{
    public static void RunAsMyService(this IWebHost Host)
    {
        var webHostService = new MyWebHostService(Host);
        ServiceBase.Run(webHostService);
    }
}

internal class MyWebHostService : WebHostService
{
    public MyWebHostService(IWebHost Host) : base(Host)
    {
    }

    protected override void OnStarting(string[] args)
    {
        base.OnStarting(args);
    }

    protected override void OnStarted()
    {
        base.OnStarted();
    }

    protected override void OnStopping()
    {
        base.OnStopping();
    }
}

Cet article Comment héberger votre ASP.NET Core dans un service Windows et des commentaires couvrent les détails.

UPDATE (voir plus de détails dans Résumé rapide des nouveautés dans ASP.NET Core 2.0 ): 

Dans ASP.NET Core 1.1 nous avions quelque chose comme ceci:

public class Program
{
    public static void Main(string[] args)
    {
        var Host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        Host.Run();
    }
}

Dans ASP.NET Core 2.0, cela ressemble à ceci:

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}
3
Dmitry Pavlov

J'ai trouvé ce sujet en cherchant une solution à un problème similaire. Nous avons fini par implémenter quelque chose qui fonctionne à peu près comme un Topshelf très (très) simplifié. Vous pouvez facilement exécuter en tant que console et/ou installer, désinstaller et ainsi en tant que service. Je pense que cela peut être très utile pour quelqu'un. Vous pouvez trouver le code ici: https://github.com/PeterKottas/DotNetCore.WindowsService et il y a un nuget ici aussi https://www.nuget.org/packages/PeterKottas.DotNetCore.WindowsService/ . Prendre plaisir :) 

2
Peter Kottas

Les services Windows peuvent être facilement créés avec Visual Studio 2017. Vous devrez déplacer votre code .NET Core vers une bibliothèque de classes qui cible NetStandard et qui peut ensuite être référencée. Votre projet de service Windows devra cibler l'intégralité du .NET Framework, mais peut faire référence à votre bibliothèque partagée. 

En plaçant tout votre code dans la bibliothèque de classes, la portabilité entre le service Windows et les applications autres que Windows est assurée.

Avec VS 2015, vous ne pouviez pas créer de projet de service Windows et référencer un .NET Core xproj. Visual Studio 2017 corrige cela une fois que vous convertissez tous vos projets en csproj.

Ce blog le décrit plus en détail: Comment créer des services Windows .NET Core avec Visual Studio 2017

2
Matt Watson

À l'aide du dernier modèle d'application Web ASP.NET Core (.NET Framework) VS2015 Update 2 et des mods de Program.cs ci-dessous, il fonctionne parfaitement lorsqu'il est exécuté en tant que service, autonome et sous VS. Notez que le chemin d'accès au répertoire contenant wwwroot doit être défini lors de l'exécution en tant que service.

Jusqu'à présent, j'aime beaucoup ce modèle et ce modèle. Peut développer le code MVC6 comme d'habitude dans Visual Studio, puis se déploie proprement en tant que service. En fait, j'ai remarqué que lors du déploiement d'une instance locale du service à des fins de test, il n'était pas nécessaire de supprimer/créer un service à l'aide de sc.exe. Arrêtez simplement le service, publiez le code et redémarrez le service et il enregistrera les nouvelles modifications.

public class Program
{
    public static void Main(string[] args)
    {
        IWebHost Host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        if (args.Contains("--windows-service"))
        {
            Host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot("<directory-containing-wwwroot>")
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseUrls("http://+:5000")
            .Build();

            Startup.is_service = true;
            Host.RunAsService();
        }
        else
        {
            Host.Run();
        }
    }
}
1
howardlo

Mise à jour pour ce que j'utilise pour .netcore 2.1, en utilisant le plus possible le modèle stock, ce qui inclut la lecture de la configuration à partir du fichier appsettings.json, des arguments de commande, du filtrage d'hôte, etc., tels qu'ils sont implémentés dans les méthodes d'assistance de l'équipe .netcore. Beaucoup de ces paramètres sont donc contrôlés via des fichiers de configuration ou des arguments de commande. Consultez la documentation de Microsoft sur ce que le modèle de stock fournit et sur la manière de remplacer les paramètres.

Veuillez noter que j'ai vu le service ne pas démarrer le service si vous définissez un point de terminaison avec https et le certificat de développement par défaut. Il peut simplement s'agir de spécifier un pfx via la configuration. Si quelqu'un veut améliorer cela, n'hésitez pas à le faire.

de toute façon voici le code de modèle stock que j'utilise dans program.cs

public class Program
{
    public static void Main(string[] args)
    {

        if (args.Contains("--service"))
        {
            var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            Directory.SetCurrentDirectory(path); // need this because WebHost.CreateDefaultBuilder queries current directory to look for config files and content root folder. service start up sets this to win32's folder.
            var Host = CreateWebHostBuilder(args).Build();
            Host.RunAsService();
        }
        else
        {
            CreateWebHostBuilder(args).Build().Run();
        }

    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();
}
0
AppLS