wake-up-neo.com

Hosting von ASP.NET Core als Windows-Dienst

Wie ich es in RC2 bekomme, gibt es eine Unterstützung für das Hosting von Anwendungen innerhalb von Windows Services. Ich habe versucht, es an einem einfachen Web-API-Projekt (mit .NET Framework 4.6.1) zu testen.

Hier ist mein Program.cs-Code:

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() {}
  }
}

Alle anderen Elemente stammen im Wesentlichen aus der .NET Core-Vorlage (obwohl ich das Framework in net461 geändert und einige Abhängigkeiten in project.json hinzugefügt habe). 

Nach dem Veröffentlichen mit dotnet publish und dem Erstellen des Windows-Dienstes mit sc create kann ich meinen Dienst erfolgreich starten, aber ich kann keinen meiner Controller erreichen (Ports werden nicht aufgelistet). Ich gehe davon aus, dass ich etwas falsch mache. 

Ich denke also, die Hauptfrage ist, wie man selbst gehostete Web-APIs erstellt und als Windows-Dienst ausführt. Alle gefundenen Lösungen funktionieren nach dem RC2-Update nicht mehr. 

26
Maria P

Sie haben hier ein paar Optionen - verwenden Sie die WebHostService-Klasse von Microsoft, übernehmen Sie den WebHostService oder schreiben Sie einen eigenen. Letzteres ist darauf zurückzuführen, dass wir mit der Microsoft-Implementierung keine generische Erweiterungsmethode mit einem Typparameter schreiben können, der WebHostService erbt, da diese Klasse keinen parameterlosen Konstruktor enthält und auch nicht auf den Service Locator zugreifen kann.

Verwendung von WebHostService

In diesem Beispiel werde ich eine Konsolenanwendung erstellen, die Microsoft.DotNet.Web.targets verwendet, eine EXE-Datei ausgibt und als MVC-App (Ansichten, Controller usw.) fungiert. Ich gehe davon aus, dass das Erstellen der Konsolenanwendung, das Ändern der Ziele in .xproj und das Ändern von project.json für die richtigen Veröffentlichungsoptionen und das Kopieren der Ansichten, Controller und des Webroots aus der Standardvorlage der .NET Core-Webanwendung einfach ist. 

Nun das Wesentliche:

  1. Erhalten Sie this package und stellen Sie sicher, dass Ihr Framework in der Datei project.json net451 (oder neuer) ist.

  2. Stellen Sie sicher, dass das Inhaltsstammverzeichnis am Einstiegspunkt ordnungsgemäß auf das Veröffentlichungsverzeichnis der EXE-Anwendungsdatei festgelegt ist und dass die Erweiterungsmethode RunAsService () aufgerufen wird. Z.B:

    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();
        }
    }
    

Die .exe kann jetzt einfach mit dem folgenden Befehl installiert werden

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

Sobald der Dienst gestartet ist, wird die Webanwendung gehostet und findet ihre Ansichten erfolgreich und rendert sie.

Vererben von WebHostService

Ein wesentlicher Vorteil dieses Ansatzes ist, dass wir die OnStopping-, OnStarting- und OnStarted-Methoden überschreiben können.

Nehmen wir an, dass das Folgende unsere benutzerdefinierte Klasse ist, die WebHostService erbt 

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();
    }
}

Nach den obigen Schritten müssen wir unsere eigene Erweiterungsmethode aufschreiben, die den Host mit unserer benutzerdefinierten Klasse ausführt:

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

Die einzige Zeile, die im vorherigen Beispiel des Einstiegspunkts noch geändert werden muss, ist die else -Anweisung am Ende. Sie muss die richtige Erweiterungsmethode aufrufen

Host.RunAsCustomService();

Installieren Sie schließlich den Dienst mit den oben beschriebenen Schritten.

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

Wenn nur das vollständige .NET Framework ausgeführt werden kann, sind die vorherigen Antworten ausreichend (Erben von ServiceBase oder Verwenden von Microsofts WebHostService).

Wenn Sie jedoch nur mit .NET Core laufen möchten/wollen (z. B. auf einem Windows-Nano-Server, einer einzelnen Binärdatei, die unter Linux als normale App und als Dienst unter Windows ausgeführt wird oder nebeneinander ausgeführt werden muss Bei Apps mit einer alten .NET Framework-Version (damit Sie das System-Framework nicht aktualisieren können), müssen Sie die entsprechenden Windows-API-Aufrufe über P/Invokes selbst vornehmen. Da ich das tun musste, habe ich eine Bibliothek erstellt, die genau das tut.

Es gibt ein sample , das sich auch selbst installieren kann, wenn es mit Administratorrechten ausgeführt wird (z. B. dotnet MyService.dll --register-as-service).

8
Martin Ullrich

Sie müssen auf Microsoft.AspNetCore.Hosting und Microsoft.AspNetCore.Hosting.WindowsServices verweisen und diesen Code in Ihrer Startklasse verwenden, damit er als Windows-Dienst ausgeführt werden kann:

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();
    }
}

Dann müssen Sie eine Erweiterungsmethode für IWebHost.RunAsService hinzufügen, um sie mit Ihrer benutzerdefinierten WebHostService-Klasse mit OnStopping und OnStarting-Ereignisbehandlungsroutinen zu umschließen:

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();
    }
}

Dieser Beitrag So hosten Sie Ihren ASP.NET Core in einem Windows-Dienst und Kommentare beziehen sich auf die Details.

UPDATE (Weitere Informationen finden Sie in Kurze Zusammenfassung der Neuerungen in ASP.NET Core 2.0 ): 

In ASP.NET Core 1.1 hatten wir so etwas:

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

        Host.Run();
    }
}

In ASP.NET Core 2.0 sieht das so aus:

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

Ich habe dieses Thema gefunden, als ich nach einer Lösung für ein ähnliches Problem gesucht habe. Am Ende etwas implementiert, das ziemlich wie sehr (sehr) vereinfachtes Topshelf funktioniert. Sie können problemlos als Konsole ausgeführt und/oder als Dienst deinstalliert und deinstalliert werden. Ich denke, es kann für jemanden ziemlich nützlich sein. Den Code finden Sie hier: https://github.com/PeterKottas/DotNetCore.WindowsService und es gibt auch hier Nuget https://www.nuget.org/packages/PeterKottas.DotNetCore.WindowsService/ . Genießen :) 

2
Peter Kottas

Windows Services können mit Visual Studio 2017 problemlos erstellt werden. Sie müssen Ihren .NET Core-Code in eine Klassenbibliothek verschieben, die auf NetStandard abzielt, auf das dann verwiesen werden kann. Ihr Windows-Dienstprojekt muss auf das gesamte .NET Framework abzielen, kann jedoch auf Ihre gemeinsam genutzte Bibliothek verweisen. 

Wenn Sie den gesamten Code in der Klassenbibliothek ablegen, haben Sie dann Portabilität zwischen dem Windows-Dienst und anderen Nicht-Windows-Apps.

Mit VS 2015 konnten Sie kein Windows Service-Projekt erstellen und auf ein .NET Core-Xproj verweisen. Visual Studio 2017 behebt das Problem, sobald Sie alle Ihre Projekte in csproj konvertieren.

In diesem Blog wird ausführlicher darauf eingegangen: Erstellen von .NET Core Windows Services mit Visual Studio 2017

2
Matt Watson

Wenn Sie die neueste Vorlage für die ASP.NET Core-Webanwendung (.NET Framework) von VS2015 Update 2 und die Mods to Program.cs verwenden (siehe unten), funktioniert es einwandfrei, wenn es als Dienst, als eigenständige Anwendung und als VS ausgeführt wird. Beachten Sie, dass der Pfad zu dem Verzeichnis, das wwwroot enthält, festgelegt werden muss, wenn es als Dienst ausgeführt wird.

Bisher mag ich diese Vorlage und dieses Muster wirklich. Kann MVC6-Code wie üblich in Visual Studio entwickeln und dann als Dienst sauber bereitstellen. Tatsächlich ist mir aufgefallen, dass bei der Bereitstellung einer lokalen Instanz des Dienstes zum Testen keine Notwendigkeit besteht, den Dienst mithilfe von sc.exe zu löschen/zu erstellen. Stoppen Sie einfach den Dienst, veröffentlichen Sie den Code und starten Sie den Dienst erneut. Die Änderungen werden übernommen.

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

Für das, was ich für .netcore 2.1 verwende, wurde so viel wie möglich von der Stock-Vorlage verwendet, was das Lesen der Konfiguration von appsettings.json, Befehlsargs, Host-Filterung usw., wie in den Hilfemethoden des .netcore-Teams implementiert, umfasst. Viele dieser Einstellungen werden daher über Konfigurationsdateien oder Befehlsargumente gesteuert. Lesen Sie in der Microsoft-Dokumentation nach, was die Vorlage für Vorräte bietet und wie Sie die Einstellungen überschreiben.

Bitte beachten Sie, dass der Dienst nicht gestartet werden kann, wenn Sie einen Endpunkt mit https und dem Standardentwicklungszertifikat definieren. Es kann einfach eine Frage der Angabe eines pfx über die Konfiguration sein. Wenn jemand dies verbessern möchte, können Sie dies gerne tun.

jedenfalls ist hier der Code für die Bestandsvorlage, den ich in program.cs verwende

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