Use HostBuilder.ConfigureServices with DryIoc container?

3.9k views Asked by At

I have some class libraries that provide services to the applications I create and for legacy reasons they are tightly bound to DryIoc. That is, the service registrations are tightly bound, not the actual services.

If I can I would rather not just go around changing that code if I don't have to.

Creating a new ASP.NET MVC Core application I was able to use DryIoc by changing the ConfigureServices method to return an IServiceProvider like this:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddControllersAsServices();

    var container = new Container().WithDependencyInjectionAdapter(services);
    return container.ConfigureServiceProvider<CompositionRoot>();
}

(this is from memory so may not be 100% correct but that is not important)

The important change was that the void method could be changed to return an IServiceProvider, which DryIoc can provide me with.

However, with HostBuilder, which I want to use for console applications, background services, etc. the method that configures services doesn't accept IServiceProvider so I'm not sure how to do it.

The important code is this:

var builder = new HostBuilder()
    .ConfigureAppConfiguration((hostingContext, config) => { ... })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddOptions();
        // configure services
    })
    .ConfigureLogging((hostingContext, logging) => { ... });

The ConfigureServices method above has one overload and an extension method:

  • ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
  • ConfigureServices(this IHostBuilder hostBuilder, Action<IServiceCollection> configureDelegate)

There doesn't seem to be any provisions for returning or using a IServiceProvider or anything else that DryIoc could provide for me in this regard.

Is this possible? Is there a way to bridge the gap? Or do I just have to switch to using the Microsoft IServiceCollection for my class libraries? Since they are in use in many projects I'd rather not change just because it seems easiest in this specific instance, but if I have to, I have to.

1

There are 1 answers

8
Lasse V. Karlsen On

An answer, that was incorrect (but correct in the context of an ASP.NET application), was provided by @Nkosi, and the comment thread sparked a discussion about a method UseServiceProviderFactory that turned out to be the solution, so thanks guys.

To use UseServiceProviderFactory I had to implement one class myself, and add the appropriate nuget package references to my project.

Here are the steps:

  1. Add a reference to DryIoc.dll (unsurprisingly)
  2. Add a reference to DryIoc.Microsoft.DependencyInjection
  3. Change the registration code
  4. Provide a custom implementation of the framework required for UseServiceProviderFactory

The original code looked like this:

var builder = new HostBuilder()
    .ConfigureAppConfiguration((hostingContext, config) => { ... })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddOptions();
        // configure services
    })
    .ConfigureLogging((hostingContext, logging) => { ... });

Here's what to use instead:

var builder = new HostBuilder()
    .ConfigureAppConfiguration((hostingContext, config) => { ... })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddOptions();
        // configure services
    })

    //////////////////// ADD THIS vvvvvvvv
    .UseServiceProviderFactory(new DryIocServiceProviderFactory())
    .ConfigureContainer<Container>((hostContext, container) =>
    {
        container.Register<...>();
    })
    //////////////////// ADD THIS ^^^^^^^^


    .ConfigureLogging((hostingContext, logging) => { ... });

Then supply this implementation of DryIocServiceProviderFactory:

internal class DryIocServiceProviderFactory : IServiceProviderFactory<IContainer>
{
    public IContainer CreateBuilder(IServiceCollection services)
        => new Container().WithDependencyInjectionAdapter(services);

    public IServiceProvider CreateServiceProvider(IContainer containerBuilder)
        => containerBuilder.ConfigureServiceProvider<CompositionRoot>();
}

The CompositionRoot class above is resolved during configuration and the constructor can be used to configure the container. A dummy-class that does nothing can be used.