Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core in a Worker Service doesn't call Configure IApplicationBuilder

I was migrating an ASP.NET Core App to the Worker Service template and was intending to keep the Startup code. However after

within Program I kept the CreateHostBuilder as described on the MS Docs:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

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

while debugging, the ConfigureServices is being called

public void ConfigureServices(IServiceCollection services)

but the Configure within the Startup, is not reached / called

public void Configure(IApplicationBuilder app)

before it crashes calling Run().

I also tried this with the same result:

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureWebHostDefaults(webBuilder => webBuilder.Configure(Startup.Configure))
        .ConfigureServices(
            services =>
            {
                Startup.ConfigureServices(services);
                services.AddHostedService<Worker>();
            });

The interesseting part is that the following code actually calls the Startup.Configure(IApplicationBuilder app):

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureWebHostDefaults(webBuilder => webBuilder.Configure(Startup.Configure));

As soon as I am adding ConfigureServices it skips the IApplicationBuilder configuration call.

Am I missing something or what is the suggested way to achieve this?

Edit:

the exact error is:

InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Builder.IApplicationBuilder' while attempting to activate 'Kledex.Extensions.KledexAppBuilder'.

stack trace:

at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable'1 serviceDescriptors, ServiceProviderOptions options) at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder) at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter'1.CreateServiceProvider(Object containerBuilder) at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider() at Microsoft.Extensions.Hosting.HostBuilder.Build()

the error happens as soon as it reaches CreateHostBuilder(args).Build().Run(); and tries to resolve the registered services, while the above one has a dependency to some config app.UseSomething(); within the Startup.Configure() method.

A breakpoint in Startup.Configure() doesn't get hit.

like image 954
Raul Avatar asked May 12 '20 22:05

Raul


People also ask

What is IApplicationBuilder in .NET Core?

UseExceptionHandler(IApplicationBuilder) Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline. The request will not be re-executed if the response has already started.

What is CreateHostBuilder in ASP.NET Core?

The host is typically configured, built, and run by code in the Program class. The Main method: Calls a CreateHostBuilder method to create and configure a builder object. Calls Build and Run methods on the builder object.

What is IHostBuilder in ASP.NET Core?

ConfigureAppConfiguration(IHostBuilder, Action<IConfigurationBuilder>) Sets up the configuration for the remainder of the build process and application. This can be called multiple times and the results will be additive. The results will be available at Configuration for subsequent operations, as well as in Services.

How do I host an ASP NET Core app on Windows?

An ASP.NET Core app can be hosted on Windows as a Windows Service without using IIS. When hosted as a Windows Service, the app automatically starts after server reboots. The ASP.NET Core Worker Service template provides a starting point for writing long running service apps.

What is ASP NET Core configuration?

Configuration in ASP.NET Core. The Startup class. ASP.NET Core apps use a Startup class, which is named Startup by convention. The Startup class: Optionally includes a ConfigureServices method to configure the app's services. A service is a reusable component that provides app functionality.

How do I create a worker in ASP NET Core?

Select App under .NET Core in the sidebar. Select Worker under ASP.NET Core. Select Next. Select .NET Core 3.0 or later for the Target Framework. Select Next. Provide a name in the Project Name field. Select Create.

How to implement a console-like service in ASP NET Core?

In ASP.NET Core, we can implement such a service which runs similar to a console application by implementing the IHostedService interface or by simply overriding required methods in the BackgroundService abstract class.


Video Answer


3 Answers

Here's your problem:

You are trying to use a pattern that was specifically designed for ASP.NET Core, but you aren't using ASP.NET Core and because of that you aren't getting the Startup pattern or any of it's features such as IApplicationBuilder or IWebHostEnvironment.

A while back, someone asked if we could figure out how to get the Startup pattern of ASP.NET Core in to something that uses the generic host builder without the use of ASP.NET Core. I came up with an answer.

The above answer doesn't give you the Configure(IApplicationBuilder app, IWebHostEnvironment env) method, and it shouldn't. That method is specifically for a Web Host. Not a worker. There is no reason you should need this method unless you are starting up a Web Host.

And if that's the case, then you should start over and create a brand new ASP.NET Core application... Not a Worker. This will configure your application to use Microsoft.NET.Sdk.Web SDK and not Microsoft.NET.Sdk.Worker SDK giving you the features you lost by going the Worker route.

You will not get an answer that will work for you unless you move away from the Worker SDK and move to the ASP.NET Core SDK. Maybe someone will come up with something, but more than likely it will be a kludge. And kludges break overtime and are a PITA to maintain.

There is nothing wrong with using Microsoft.NET.Sdk.Web for a Windows service. Microsoft even shows you how (Specifically pay attention to Deployment Type section).

like image 116
Andy Avatar answered Oct 13 '22 12:10

Andy


Given that you are trying to configure web host defaults and use ASP.NET Core features like IApplicationBuilder, I believe that you are planning to run the service over some kind of web server. Is this correct?

If so, besides the other good suggestions, I have another one, which is to keep using an ASP.NET Core app - obviously enabling the usage of the Startup almost as is (with ConfigureServices and Configure methods) and perhaps most of your previous app.

To do so, in your Worker csproj, you could replace <Project Sdk="Microsoft.NET.Sdk.Worker"> by <Project Sdk="Microsoft.NET.Sdk.Web">.

Then, you can configure your CreateHostBuilder such as:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(
            webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Please note that I removed UseWindowsService() on purpose, since I am describing a case of using Worker services and not specifically Windows services.

Now you can keep using your Startup class, adding the registration of your background service(s):

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        // ...
    }
    
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.AddHostedService<Worker>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...
    }
}

Hope this helps :) However, if you specifically want to develop Windows services rather than Worker services, please let us know.

In this case, we keep having an ASP.NET Core WebApp/WebAPI with all its capabilities. We just added background services to it :)

like image 22
nunohpinheiro Avatar answered Oct 13 '22 11:10

nunohpinheiro


Yes, I had the same issue.

Answer for the question we can find in the Microsoft documentation here.

We need to use method ConfigureAppConfiguration for Host Services.

Please review my code examples. We use this code and it works fine:


Program.cs:

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureAppConfiguration((context, builder) =>
            {
                var environment = context.HostingEnvironment.EnvironmentName;
                builder.AddAppSettings(environment);
            })
            .ConfigureWebHostDefaults(builder =>
            {
                builder.UseStartup<Startup>();
            });
}

HostedService

public class HostedService : IHostedService
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private readonly ILogger<HostedService> _logger;

    public HostedService(IServiceScopeFactory serviceScopeFactory, ILogger<HostedService> logger)
    {
        _serviceScopeFactory = serviceScopeFactory;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Service is starting...");

        // Run new task and use your business logic
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            // Resolve business manager class with many dependencies. It is Scoped LifeTime.
            var manager = scope.ServiceProvider.GetRequiredService<IClientManager>();
        }

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Service is stopping...");
        return Task.CompletedTask;
    }
}

enter image description here


So we had only one trouble with this approach, we can inject only singletons to HostedService class. We will inject IServiceScopeFactory to constructor. And after it we create scope, resolve all needed dependencies and run our business logic.

like image 22
Alexander I. Avatar answered Oct 13 '22 12:10

Alexander I.