Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hosting Web API in .Net Core Worker Service - cannot reference IWebHostEnvironment

I'm creating a .NET Core Worker Service, and want to expose ASP.Net Core Web APIs for the service. I'm using .NET Core 3.0. Initially, my plan was to replace IHostBuilder with IWebHostBuilder and add a Startup class just like a regular Asp.Net Core web app (this is probably an oversimplification, though).

My plan was to simply try to replace

public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)                
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
        }

with

public static IWebHostBuilder CreateHostBuilder(string[] args)
        {
            return WebHost.CreateDefaultBuilder(args)
                .ConfigureServices(services =>
                {
                    services.AddHostedService<Worker>();
                }).UseStartup<Startup>();            
        }

which may not work at all, but at least it's a starting point... What's blocking me from trying this approach is that I cannot implement my Startup class because IWebHostEnvironment is unavailable.

Here's my

<PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UserSecretsId>dotnet-WorkerServices-0E977A2C-F0C8-49E7-B00A-5EB01B99FBEB</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0" />
  </ItemGroup>

As far as I know, IWebHostEnvironment should be in the Microsoft.Extensions.Hosting.Abstractions nuget package, but referencing it does not seem to work.

Does anyone have any ideas? -Thanks!

like image 699
ToreS Avatar asked Oct 07 '19 17:10

ToreS


2 Answers

It is much easier to add a service to an API project. That's just one extra line.

If you can't or don't want to start over, change your project manually. Forget about IWebHostBuilder, it is now obsolete or deprecated or something.

I could post some fragments here but much easier: create a temporary API project, copy over the Program and Startup classes (but keep your namespaces) and then, inside Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<Worker>();  // the worker will run
    services.AddControllers();
}
like image 125
Henk Holterman Avatar answered Nov 15 '22 09:11

Henk Holterman


I researched a lot and I did this issue using some new methods which were not well-formed and lack performance, and I tried to find a correct way of doing this problem, finally, I solved it and I've shared my experiences in this post. You can use the following steps which are working 100%, and also, you can clone the Worker-Service Web-API template from my GitHub profile.

Actually, you needn't use IWebHostBuilder. The following steps are enough to self-host a WebAPI in a .net core worker service and host worker service in windows services:

  1. Create a simple console application.
  2. Install packages "Microsoft.AspNetCore.App" and "Microsoft.Extensions.Hosting.WindowsServices" using NuGet in your console application.
  3. Create an empty file named Worker.cs and put the following code inside it:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace WorkerServiceWebAppTemplate
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

The above class is your worker service main file which is inherited from BackgroundService, personally, I inherit IHostedService most of the time.

  1. Create another file named Startup.cs and put the following lines of codes:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;

namespace WorkerServiceWebAppTemplate
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
                app.UseDeveloperExceptionPage();
            else
                app.UseHsts();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            });
        }
    }
}

The Startup file is your web server file and it starts a host and has a simple GET API (/) returns a simple message, you can extend it.

  1. Finally, you've to start your worker service and your host at the same time. change your Program.cs like the following codes:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

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

        private static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureServices((hostBuilderContext, services) =>
                {
                    services.AddHostedService<Worker>();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
        }
    }
}

The following lines in your Program.cs file do the magic:

                .UseWindowsService()
                .ConfigureServices((hostBuilderContext, services) =>
                {
                    services.AddHostedService<Worker>();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
like image 39
Saeed Aghdam Avatar answered Nov 15 '22 08:11

Saeed Aghdam