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.
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.
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.
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.
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.
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.
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.
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.
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).
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 :)
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;
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With