I'm trying to create a service which will run as a Windows service as described here. My problem is that the example Web Host Service constructor only takes an IWebHost parameter. My service needs a constructor more like this:
public static class HostExtensions
{
public static void RunAsMyService(this IWebHost host)
{
var webHostService =
new MyService(host, loggerFactory, myClientFactory, schedulerProvider);
ServiceBase.Run(webHostService);
}
}
My Startup.cs file looks similar to this:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.AddInMemoryCollection();
this.Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
this.container.RegisterSingleton<IConfiguration>(this.Configuration);
services.AddSingleton<IControllerActivator>(
new SimpleInjectorControllerActivator(container));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSimpleInjectorAspNetRequestScoping(this.container);
this.container.Options.DefaultScopedLifestyle = new AspNetRequestLifestyle();
this.InitializeContainer(app, loggerFactory);
this.container.Verify();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
private void InitializeContainer(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
container.Register(() => loggerFactory, Lifestyle.Singleton);
container.Register<IMyClientFactory>(() => new MyClientFactory());
container.Register<ISchedulerProvider>(() => new SchedulerProvider());
}
Obviously I'm using Simple Injector as a DI container. It's registered with the IServiceCollection as detailed in their documentation.
My question is how do I access the framework's container (the IServicesCollection) in the HostExtensions class so that I can inject the necessary dependencies into MyService? For MVC controllers that's all just handled under the covers, but I don't know of any documentation detailing how to access it where needed elsewhere.
You just have to make a few minor tweeks to your code to get this working.
1. Make the
Containerapublic staticfield in theStartupclass:2. Move the verification out of the
Startupand into theMain:Doing this allows extra registration to be added to the container, after the
Startupclass is done with it, but before the application is actuall started:3. Register your
MyServiceas singletonRegistering it explictly as singleton in the container allows Simple Injector to run diagnostics on it and prevents accidental captive dependencies that the
MyServicemight accidentaly have. You should register it as singleton, because it will be kept alive for the duration of the application:3. Register missing dependencies that
MyServicerequires.MyServicedepends on host, loggerFactory, myClientFactory and schedulerProvider, which are not all currently registered.The
hostcan be registered inside theMainmethod:While the
loggerFactorycan be registered inside theStartupclass:I assume that the
myClientFactoryandschedulerProviderdependencies are already registered in the container.4. Replace the
RunAsMyServiceextension method with simple resolve from the containerSince the
MyServiceis successfully registered in the container with all its dependencies, we should now be able to resolve it from the container and pass it on to theServiceBase.Runmethod:That should be all.
One last note, depending on the type of application you are building, you might not need such elaborate
Startupclass at all. You might move the configuration of the container closer to theMainmethod. Whether you should do this, depends on how much you actually need.Here's an example of working without an
Startupclass: