Search code examples
autofacautofac-configuration

Export an autofac container to its identical json configuration file


Is there a way to take an existing Autofac Module, Container or ContainerBuilder and use it to generate an equivalent json file that can be loaded via configuration extensions?

For some background, we have a large code base with hundreds of registrations and dozens of configurations, each with its own json config file. Managing these files after they were deployed became a nightmare so we've since moved everything to a single autofac module with a few parameters exposed to support the different configuration combinations.

This is much more manageable, but occasionally our users need to "tweak" one of the registrations, which we can't do anymore without providing a new build.

My first thought was to give them a tool that would build up the container in code and then export its registrations to a config file that these specific users could leverage instead. I started by just trying to find something on the autofac side to decorate, so I could "intercept" registrations:

public sealed class AutofacGenerator : IModuleRegistrar
...
    public IModuleRegistrar RegisterModule(IModule module)
    {
        _modules.Add(module);        
        _decorated.RegisterModule(module);
        return this;
    }

    // extension method `ToJson` not shown
    public IEnumerable<string> Generate() => _modules.Select(module => module.ToJson()); 

Then in my top level module I add this:

public class MyAutofacModule: Module
...
    protected override void Load(ContainerBuilder builder)
    {
        Generate(builder);
    }

    public IEnumerable<string> Generate(ContainerBuilder builder)
    {
        var generator = new AutofacGenerator(decorated: builder);
        generator.RegisterModule(new MySubModule1());
        generator.RegisterModule(new MySubModule2());
        ...
        return generator.Generate();
    }

And use it like this:

var myJsonifiedModules = new MyAutofacModule().Generate()
// save these strings to a file, not shown...

Obviously this has several issues. This only exports modules, which is okay for my purposes but ideally I'd intercept all registrations. I was hoping to decorate ContainerBuilder and pass it in from the top, but it's sealed without virtual methods.

Also I'm not really decorating anything, but since Autofac's extension methods creates and uses its own concrete ModuleRegistrar this is the best I can do. There's no point in implementing their interface besides making my intent clear and keeping the method signature the same to make it easier to swap in...

The above issues mean I'm stuck having to modify my top level module (or any module I want to export) to use AutofacGenerator.

Perhaps decorating is the wrong approach. I also tried just using the Container.ComponentRegistry.Registrations and Container.ComponentRegistry.Sources directly but quickly got overwhelmed with how to translate those into json. Also, there doesn't seem to be a way to back out what modules were used, so my generated file would be one flat list of component registrations only.

I feel like I can't be the only one where the ability to export configs would be useful. Are there existing approaches to this problem I can leverage?


Solution

  • This is not a feature that exists, and it's not something Autofac will implement.

    (I'm one of the Autofac owners, so I can speak pretty authoritatively to that. Let me explain why we wouldn't implement it.)

    Configuration files are convenient in some instances, to be sure, but as noted in the documentation:

    Configuration in JSON/XML is not a feature-for-feature replacement for programmatic configuration, so complex scenarios may require a combination of JSON/XML and modules.

    And really, that's the thing.

    Modules allow full programmatic access to every feature of Autofac. You could...

    • Register a delegate.
    • Use interceptors or decorators.
    • Set up attribute filtering for keys or metadata.
    • Configure method injection.
    • Set up custom disposal logic.

    ...and so on. There is no way to export most of that stuff to configuration format. I mean, if you did, you'd end up writing a configuration language that... well, basically is C#. There's not a lot of value in that.

    You might want to consider doing something that allows folks to just modify the C# registration code directly. For example, maybe you have a .cs file with all of your registrations in it and use the System.CodeDom namespace to compile it at app startup and use that as the code to execute for registrations. Naturally this has some security implications - someone could tweak that to do Very Bad Things in your app, so be careful.

    But no, sorry, there's no feature that will just export a module.