We have legacy application that we plan to transition to the ASP.NET Core API (v2.2) some time in future. In order to make it easier, we decided to start using Generic Host with all the available features for all future enhancements (appsettings/logging/DI etc.). The idea is that once we switch to the API, we will copy the host configuration and majority of the code will keep working. We will expose the host.Services.GetService() to the legacy codebase which will use it as a simple ServiceLocator.
Question is: is there any downside of using such approach? Also, do we need to use Start/Stop/Run on the generic host? Based on what I've read, this is only needed for running the IHostedService which we don't plan to use. I tested if it works without calling Start/Stop and everything seems to be fine but all the examples I found do call Start/Stop, even without IHostedService.
private IHost _host;
private PricingHost()
{
_host = new HostBuilder()
.UseEnvironment("BETA")
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.SetBasePath(System.IO.Directory.GetCurrentDirectory());
configApp.AddJsonFile("appsettings.json", optional: true);
configApp.AddJsonFile(
$"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",
optional: true);
})
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
configLogging.AddDebug();
configLogging.AddConsole();
configLogging.AddCustomLogger();
})
.ConfigureServices((hostContext, configServices) =>
{
var startup = new Startup(hostContext.Configuration);
startup.ConfigureServices(configServices);
})
.Build();
}
public T GetService<T>()
{
return _host.Services.GetService<T>();
}
public T GetRequiredService<T>()
{
return _host.Services.GetRequiredService<T>();
}
UPDATE: let me try to clarify. We have legacy, 10years old windows service. We plan to transition it to asp.net .net core api some time in the future BUT there is still an ongoing work on the windows service right now. In order to simplify future transition we want to use generic host for DI, config access, logging etc. We simply want to use it as a service locator (so no need for IHostedService) but have an ability some time in future to be able to just copy all the configuration/setup and paste it into the new API
After doing small POC I know that this is totally possible but what I'm trying to figure out is if there are any pitfalls I should be aware of and how to properly use GenericHost without IHostedService (do I need to call Run/Start/Stop at some point or it is unnecessary)?
UPDATE: Adding dot net fiddle example of what I'm doing here
Ok from my comment and your answer I will try to find an answer :)
I think if you really need to use the service locator pattern instead of a dependency injection you can also register in the dependency injection the IServiceProvider
itself
IServiceProvider
)private static IHostBuilder CreateHostBuilder()
{
// One host for all services
return new HostBuilder()
.UseEnvironment("BETA")
.ConfigureAppConfiguration((hostContext, configApp) =>
{
// your configuration
configApp.AddJsonFile("appsettings.json", optional: true);
})
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConsole();
})
.ConfigureServices((hostContext, configServices) =>
{
// your services
configServices.AddHostedService<PricingHost>();
// register the service provider as singleton
configServices.AddSingleton<IServiceProvider>(sp => sp);
});
}
IHostedService
Like you said you do not want to use IHostedService
but as I understand you want to instanciate multiple IHost
classes which is at the end very unhandy. So I think the best solution is to implement it with IHostedServices
use the code from your old projects in the StartAsync(...)
-methods and let them run "forever" eighter with a while
loop or a Task.Delay(forever)
and use the IServiceProvider
as service locator.
// Each service as IHostedService
public class PricingHost : IHostedService
{
// Usage as: Service Locator
private readonly IServiceProvider _serviceProvider;
public PricingHost(IServiceProvider sp)
{
_serviceProvider = sp;
}
public async Task StartAsync(CancellationToken token)
{
Task.Run(() => RunningTask(token));
await Task.Delay(Timeout.Infinite, token);
}
public Task StopAsync(CancellationToken token)
{
return Task.CompletedTask;
}
private Task RunningTask(CancellationToken token)
{
// do your things ...
var logger = _serviceProvider.GetRequiredService<ILogger<PricingHost>>();
logger.LogInformation("Working...");
return Task.CompletedTask;
}
}
Maybe it is more clear if you look to my short Example on dotnet fiddle
Leave me a comment if there is anything unclear. Maybe I did not get it right why you want to have multiple hosts in your application.