2

We are developing a windows service that runs .net core 2.x. Following this blog post by Steve Gordon running .netcore generic host applications as a service things seem to be working beautifully... as long as we use the IServiceCollection. I prefer SimpleInjector but I'm not sure how I can use it like I do in asp.net core. I there's a way to replace the built in DI as described here Default service container replacement and I know the SI team doesn't recommend the approach ASP.NET Core MVC Integration Guide so is there a better way in this use case?

Here is what I have so far but it's uncomfortable

--main program

internal class Program
{
    private static async Task Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Runner>();
                //configure SimpleInjector here???
            });

        if (isService)
        {
            await builder.RunAsServiceAsync();
        }
        else
        {
            await builder.RunConsoleAsync();
        }
    }
}

Configuring the container here works more or less but the first class being created by the host (i.e. Runner in this case) gets created by the Host and injects any dependencies via the IServicesCollection. So my question is how do I have it inject from my SI container instead?

7
  • I'm not sure I follow your question and what the problem is you are encountering. While integrating Simple Injector with ASP.NET Core, you would never replace the built-in container, and so shouldn't you with something that runs as a service. So you'd follow an identical approach where you would plug in into the framework by replacing one of its main interception points, such as IControllerActivator. So what is preventing you from taking that approach? Commented Sep 19, 2018 at 19:17
  • 1
    @Steven yeah I agree the question was a bit vague and I was hoping the blog post from Steven Gordon would make it clear. I have edited the question and will post the now obvious answer in a minute Commented Sep 20, 2018 at 12:23
  • @Steven also, the host builder here doesn't have an IControllerActiviator because I'm not using ASP.NET core. :) Commented Sep 20, 2018 at 12:29
  • But are you doing than? A request comes in, and what happens at that point? Commented Sep 20, 2018 at 12:57
  • @Steven ah, the "requests" are actually rebus messages. So my bootstrapper configures the service bus (rebus) which does the polling and uses the SI adapter to build up the message handler dependencies as required. Commented Sep 20, 2018 at 17:56

3 Answers 3

1

The obvious answer here is... Don't have any dependencies injected into the Runner. Instead Runner is the class that represents your application entry point so I configure my container there and dispose of it when the Runner is stopped. The complete code for runner...

public class Runner : IHostedService, IDisposable
{
    private Container _container;
    public Runner()
    {
        _container = new Container();
        _container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Bootstrapper.Bootstrap(_container);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _container.Dispose();
        _container = null;
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Wouldn't this mean that Runner is now un-testable?
0

I would hook onto the ConfigureContainer method of the HostBuilder and setup simpleinjectore there likke this:

                   HostBuilder()
                   .ConfigureContainer<ServiceCollection>((builder, services) =>
                   {
                       var container = new Container();

                       container.RegisterSingleton<IJobRepository, JobRepository>();
                       services.AddTransient<IHostedService, TimedService>();

                   })
                   .ConfigureServices((hostContext, services) =>
                   {
                       // Originally we would have done this
                       //services.AddHostedService<Service>();
                   })
                   .Build();

        using (host)
        {
            await host.StartAsync();
            await host.WaitForShutdownAsync();
        }

While you could use your IHostedService implementation indeed I think it may hide what is going on. I believe the infrastructure bootstrapping should be done in one place or orchestrated at least in one place. I consider the container to be infrastructure and would set it all up with the rest of the app via the HostBuilder methods.

An added advantage may also be that you do not entirely replace the ServiceCollection as it works well with doing other framework related things. An example of some stuff I would still do with the ServiceCollection:

                   HostBuilder()
                   .ConfigureServices((hostContext, services) =>
                   {
                       services.AddLogging();
                       services.AddOptions();
                   })

This is in line with what is stated in the simpleinjector docs about setting the container up with ASP.NET Core:

The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in container build framework and third-party components,The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in container build framework and third-party components

The same should apply with just .net core and the generic HostBuilder.

Comments

0

Generic host resolves hosted services from services collection, so the solution is to register hosted services in Simple Injector and then resolve them from Simple Injector to register in Services collection:

var container = new Container();
var host = new HostBuilder()
//...
    .ConfigureServices((context, services) =>
    {
        container.Collection.Append(typeof(IHostedService), typeof(Runner));
        services.AddSingleton(_ => container.GetAllInstances<IHostedService>());
    })
//...
    .Build();

container.Verify();
await host.RunAsync();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.