16

I am currently hosting an ASP.NET (.NET 5) application in a Windows Service using WebHost and WebHostBuilder.

.NET 6 introduced WebApplication and WebApplicationBuilder. How can I use these to host in a Windows Service?

Sandy
  • 1,284
  • 2
  • 14
  • 32
  • The same way you used the `WebHost`/`WebHostBuilder`. – Guru Stron Jan 03 '22 at 22:17
  • @GuruStron Here is the .NET source code for hosting in a Windows Service. As you can see is based on IWebHost. Can you show me how to use WebApplication instead of IWebHost? https://github.com/dotnet/aspnetcore/blob/4e7d976438b0fc17f435804e801d5d68d193ec33/src/Hosting/WindowsServices/src/WebHostWindowsServiceExtensions.cs – Sandy Jan 04 '22 at 00:15

2 Answers2

38

You would use the UseWindowsService() extension method documented here:

https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&tabs=visual-studio#app-configuration

As is the case in previous versions, you need to install the Microsoft.Extensions.Hosting.WindowsServices NuGet package.

Implementing this in .NET 6 with WebApplicationBuilder requires a workaround:

var webApplicationOptions = new WebApplicationOptions() { ContentRootPath = AppContext.BaseDirectory, Args = args, ApplicationName = System.Diagnostics.Process.GetCurrentProcess().ProcessName };
var builder = WebApplication.CreateBuilder(webApplicationOptions);

builder.Host.UseWindowsService();

Updated:

Microsoft has changed how services should be created as the fix for this. The --contentRoot argument should be used with the exe.

sc config MyWebAppServiceTest binPath= "$pwd\WebApplication560.exe --contentRoot $pwd\"
New-Service -Name {SERVICE NAME} -BinaryPathName "{EXE FILE PATH} --contentRoot {EXE FOLDER PATH}" -Credential "{DOMAIN OR COMPUTER NAME\USER}" -Description "{DESCRIPTION}" -DisplayName "{DISPLAY NAME}" -StartupType Automatic
Josh Close
  • 22,935
  • 13
  • 92
  • 140
aweyeahdawg
  • 886
  • 9
  • 19
  • 1
    Thanks @aweyeahdawg But if I am not mistaken, that article shows how to use IHostBuilder whereas I would like to use WebApplicationBuilder. – Sandy Jan 06 '22 at 02:34
  • 1
    I updated my answer with the code you'd need to add – aweyeahdawg Jan 06 '22 at 02:51
  • 1
    Looks promising @aweyeahdawg Going to try it and accept answer if it works. – Sandy Jan 06 '22 at 04:03
  • 9
    Thank you @aweyeahdawg Your answer works but there is a trick to it. Calling UseWindowsService() changes the content root which is not supported using WebApplicationBuilder.Host. I had to do this: WebApplicationOptions webApplicationOptions = new() { ContentRootPath = AppContext.BaseDirectory, Args = args, ApplicationName = System.Diagnostics.Process.GetCurrentProcess().ProcessName }; var builder = WebApplication.CreateBuilder(webApplicationOptions); builder.Host.UseWindowsService(); – Sandy Jan 06 '22 at 05:55
  • 2
    @Sandy this is what I was looking for. Had everything else, but, could not figure out what the h*ck was going on. – fafafooey Feb 03 '22 at 20:27
  • 1
    @Sandy thanks for your comment, I've search all over for how to do this. If anyone is able to edit the accepted answer (I can't) to include this I think it would be helpful to other people. – jondow Feb 21 '22 at 11:16
  • @sandy can you post a separate working answer ? – kofifus Mar 02 '22 at 01:08
  • @Sandy...I think people frown at the "Thank you", but you saved my you-know-where. Thank you! – Johnny Wu Jul 11 '22 at 17:47
  • Surprisingly, for me adding `--contentRoot` command line option eliminated any need to mess with `WebApplicationOptions`, but the opposite was not true: I got exception when I was setting the content root through `WebApplicationOptions` without specifying the command line option. Anyway, I hope the developers will straighten this up soon... Cause none of these workarounds looks good. – C-F Oct 04 '22 at 22:05
  • I have read the referenced Microsoft doc for asp.net 7, but it's very vague and not step-by-step. Can anyone share a working sample code, please? Thanks! – Siavash Mortazavi Jan 08 '23 at 22:07
0

I had an existing console app that was only running an Microsoft.Extensions.Hosting.IHostedService implementation targeting .Net 6 and wanted to add a web interface that would be available when the service was running. I think this is what you're doing.

Steps

  1. Switching the Sdk attribute on the root node of the project file enter image description here
  2. Reload the project
  3. Updated the code in Program.cs

New Program Class

using MyWindowsService.App;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

// Create the builder from the WebApplication but don't call built yet
var appBuilder = WebApplication.CreateBuilder(args);

// Add the windows service class as a HostedService to the service collection on the builder
appBuilder.Services.AddHostedService<MyWindowsService>();

// Build the builder to get a web application we can map endpoints to
var app = appBuilder.Build();

// Map an endpoint
app.MapGet("/", () => "Hello World!");

// Run the web application 
app.Run();

Old Program Class

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyWindowsService.App
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<MyWindowsService>();
                });
        }
    }
}
Troy
  • 557
  • 3
  • 15
  • 1
    Hi Troy, your answer is actually what I was looking for, thanks! Could you confirm 2 things? A. Is MyWindowsService deriving from BackgroundService and has an ExecuteAsync() method? B. Doesn't the appBuilder need a "appBuilder.Host.UseWindowsService()" for it to work? (Some older tutorials are mentioning this, but it might be just an old requirement... Cheers! – Siavash Mortazavi Jan 08 '23 at 20:57
  • A. MyWindowsService only implements the interface IHostedService B. No. The Program.cs code in this answer triggered the `Start` and `Stop` functions of the service as expected. – Troy Jan 09 '23 at 15:41
  • 1
    Interesting! BackgroundService in ver 7.x has default implementations for IHostedService's StartAsync and StopAsync, so a little bit better for me. Then, Microsoft official sample was not working for me, and adding builder.Host.UseWindowsService(); did the magic trick. UseWindowsService() gradually calls an AddWindowsServiceLifetime() internal method, which sounds to be essential... Thanks again! – Siavash Mortazavi Jan 10 '23 at 17:08