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
Container
apublic static
field in theStartup
class:2. Move the verification out of the
Startup
and into theMain
:Doing this allows extra registration to be added to the container, after the
Startup
class is done with it, but before the application is actuall started:3. Register your
MyService
as singletonRegistering it explictly as singleton in the container allows Simple Injector to run diagnostics on it and prevents accidental captive dependencies that the
MyService
might 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
MyService
requires.MyService
depends on host, loggerFactory, myClientFactory and schedulerProvider, which are not all currently registered.The
host
can be registered inside theMain
method:While the
loggerFactory
can be registered inside theStartup
class:I assume that the
myClientFactory
andschedulerProvider
dependencies are already registered in the container.4. Replace the
RunAsMyService
extension method with simple resolve from the containerSince the
MyService
is 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.Run
method:That should be all.
One last note, depending on the type of application you are building, you might not need such elaborate
Startup
class at all. You might move the configuration of the container closer to theMain
method. Whether you should do this, depends on how much you actually need.Here's an example of working without an
Startup
class: