8

I'm struggling with how to organize my Autofac component registrations in modules given that some of the modules themselves have dependencies.

I've implemented an abstraction of configuration data (i.e. web.config) in an interface:

interface IConfigurationProvider
{
    T GetSection<T>(string sectionName)
        where T : System.Configuration.ConfigurationSection;
}

along with implementations for ASP.NET (WebConfigurationProvider) and "desktop" applications (ExeConfigurationProvider).

Some of my autofac modules then require an IConfigurationProvider as a constructor parameter, but some don't:

class DependentModule : Module
{
    public DependentModule(IConfigurationProvider config)
    {
        _config = config;
    }

    protected override void Load(ContainerBuilder builder)
    {
        var configSection = _config.GetSection<CustomConfigSection>("customSection");
        builder.RegisterType(configSection.TypeFromConfig);
    }

    private readonly IConfigurationProvider _config;
}

class IndependentModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.Register(/* other stuff not based on configuration */);
    }
}

Since the RegisterType() extension method doesn't accept a registration delegate (Func<IComponentContext, T>), like Register() does, I can't register the IConfigurationProvider up-front and then resolve it when I go to register the type specified in the configuration, something like:

// this would be nice...
builder.RegisterType(c => c.Resolve<IConfigurationProvider>().GetSection<CustomConfigSection>("sectionName").TypeFromConfig);

This means that I need to be able to register modules with and without a dependency on IConfigurationProvider.

It's obvious how to manually instantiate each module and register it:

IConfigurationProvider configProvider = ...;
var builder = new ContainerBuilder();
builder.RegisterModule(new DependentModule(configProvider));
builder.RegisterModule(new IndependentModule());
using (var container = builder.Build())
{
    ...
}

But I don't want to manually instantiate my modules - I want to scan assemblies for modules and register them automatically (as discussed in this question). So I have to use reflection to scan the assembly for IModule types, and use Activator.CreateInstance to make registerable instances. But how do I know whether or not to pass an IConfigurationProvider as a constructor parameter. And what happens when other modules have additional or different dependencies?

There's got to be a more straightforward way of accomplishing the basic task: register a type specified in some configuration provided via an interface, right? So how do I do that?

riQQ
  • 9,878
  • 7
  • 49
  • 66
David Rubin
  • 1,610
  • 1
  • 17
  • 28
  • Did you solve this in the end? I have very similar case, but my dependency is having another dependency in its turn (I want to get config from db, so I have a IConfigProvider dependent on IRepoConfig) – Learner Aug 06 '15 at 09:34
  • @Learner, I originally implemented something using a combination of Autofac and MEF modeled after Jim Bolla's answer below, but it was horrendously complex and I removed it before too long. I don't have dependent modules any longer: since my primary use case was selection based on a `Type`, I've since taken the approach of *registering* **all** the candidates, then *resolving* based on the property coming from the `ConfigurationSection`. I can send you sample code if you'd like. – David Rubin Aug 07 '15 at 19:43
  • Ok, thanks. I don't think I could use ConfigurationSection / xml in my case. – Learner Aug 07 '15 at 21:47
  • This can be helpful: https://stackoverflow.com/a/57257634/1453867 – SalientBrain Jul 29 '19 at 16:10

2 Answers2

7

You could do something like this:

using System.Collections.Generic;
using System.Linq;
using Autofac;
using Autofac.Core;
using NUnit.Framework;

namespace Yo_dawg
{
    [TestFixture]
    public class I_heard_you_like_containers
    {
        [Test]
        public void So_we_built_a_container_to_build_your_container()
        {
            var modules = GetModules();
            Assert.That(modules.Length, Is.EqualTo(4));

            var builder = new ContainerBuilder();

            foreach (var module in modules)
                builder.RegisterModule(module);

            var container = builder.Build();
        }

        private IModule[] GetModules()
        {
            var builder = new ContainerBuilder();

            var configurationProvider = new ConfigurationProvider();
            builder.RegisterInstance(configurationProvider).AsImplementedInterfaces().ExternallyOwned();

            builder.RegisterAssemblyTypes(GetType().Assembly)
                .Where(t => t.IsAssignableTo<IModule>())
                .AsImplementedInterfaces();

            using (var container = builder.Build())
                return container.Resolve<IEnumerable<IModule>>().ToArray();
        }
    }

    public class ModuleA : Module
    {
        public ModuleA(IConfigurationProvider config)
        {
        }
    }

    public class ModuleB : Module
    {
        public ModuleB(IConfigurationProvider config)
        {
        }
    }

    public class ModuleC : Module
    {
    }

    public class ModuleD : Module
    {
    }


    public interface IConfigurationProvider
    {
    }

    public class ConfigurationProvider : IConfigurationProvider
    {
    }
}
Jim Bolla
  • 8,265
  • 36
  • 54
  • Thanks for your suggestion. I pursued a similar strategy, but didn't like certain aspects of it. I'll have to revisit this approach. – David Rubin Jun 19 '12 at 15:36
2

For this scenario, Autofac's own XML configuration seems to cover the scenarios you're targeting. Adding a new IConfigurationProvider mechanism seems like reinventing this functionality already provided by the container. The basics are documented at: https://code.google.com/p/autofac/wiki/XmlConfiguration. The configuration syntax has in-built support for modules.

There's a nice alternative by Paul Stovell that allows modules to be registered in code yet receive parameters from config - see: http://www.paulstovell.com/convention-configuration. Hope this helps!

Nicholas Blumhardt
  • 30,271
  • 4
  • 90
  • 101
  • 2
    Thanks for the reply, but I don't understand how expressing my Autofac configuration in XML helps me out in this scenario. Perhaps because I slightly messed up the code in my question, and which I just edited to better express the idea that I'm trying to work with custom SectionHandler types. – David Rubin Jul 05 '12 at 18:13