Home » c# » c# – Howto use external plugins via config file (runtime configuration) with Autofac 5-Exceptionshub

c# – Howto use external plugins via config file (runtime configuration) with Autofac 5-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

I’m trying to setup a simple .NET Core 3.1 project that uses Autofac 5 IoC Container.

I’ve added a reference to latest Autofac.Configuration 5.0.0 package.

I’ve created interface, implementation and test app with a simple JSON config (following this guide):

{
  "components": [
    {
      "type": "Implementations.ImplementationN, Implementations",
      "services": [
        {
          "type": "Interfaces.InterfaceN, Interfaces"
        }
      ]
    }
  ]
}

I use the following code (according to the same guide):

using Interfaces;
using Autofac;
using Autofac.Configuration;
using Microsoft.Extensions.Configuration;
using System;

------------------------
var config = new ConfigurationBuilder();
config.AddJsonFile("config.json");

var module = new ConfigurationModule(config.Build());

var builder = new ContainerBuilder();    
builder.RegisterModule(module);

var container = builder.Build();
------------------------

But I’m getting System.InvalidOperationException:

The type ‘Implementations.ImplementationN, Implementations’
could not be found. It may require assembly qualification,
e.g. “MyType, MyAssembly”

I’ve uploaded my code to GitHub. Any help is appreciated.

P.S. Visual Studio 2019 16.4.5 Enterprise, Windows 10 1909 x64 Professional

UPDATE: To clarify more – my final goal is to have Interfaces.dll with no special references, Implementations.dll with reference to ONLY Interfaces.dll and Test.exe with reference to ONLY Interfaces.dll (and Autofac package of course). I expect Autofac to load specific class from a specific assembly (that are specified in config.json) via Reflection. That was possible with Unity Container and I expected to achieve the same with Autofac IoC.

Once again: I need an upproach without ANY project referencing Implementations.dll, I should be able to change specific implementation (via changing config.json) without recompilation.

How to&Answers:

I’ve found out this more a “.NET Core assembly loading” problem than an Autofac problem.

To cut a long story short, if the assembly isn’t specifically referenced by your app, you need to tell .NET Core assembly loader where to get it EVEN IF IT’S IN YOUR BIN FOLDER.

So if you (like me) don’t want to have a bootstrap library that references all implementation libraries that you need but you want to have runtime configuration via config file – you should do the following.

From Autofac examples:

// THIS IS THE MAGIC!
// .NET Core assembly loading is confusing. Things that happen to be in your bin folder don't just suddenly
// qualify with the assembly loader. If the assembly isn't specifically referenced by your app, you need to
// tell .NET Core where to get it EVEN IF IT'S IN YOUR BIN FOLDER.
// https://stackoverflow.com/questions/43918837/net-core-1-1-type-gettype-from-external-assembly-returns-null
//
// The documentation says that any .dll in the application base folder should work, but that doesn't seem
// to be entirely true. You always have to set up additional handlers if you AREN'T referencing the plugin assembly.
// https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/corehost.md
//
// To verify, try commenting this out and you'll see that the config system can't load the external plugin type.
var executionFolder = Path.GetDirectoryName(typeof(Program).Assembly.Location);
AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext context, AssemblyName assembly) =>
{
     // DISCLAIMER: NO PROMISES THIS IS SECURE. You may or may not want this strategy. It's up to
     // you to determine if allowing any assembly in the directory to be loaded is acceptable. This
     // is for demo purposes only.
     return context.LoadFromAssemblyPath(Path.Combine(executionFolder, $"{assembly.Name}.dll"));
 };

And after that the following code:

 var config = new ConfigurationBuilder()
     .AddJsonFile("autofac.json")
     .Build();
 var configModule = new ConfigurationModule(config);
 var builder = new ContainerBuilder();
 builder.RegisterModule(configModule);
 var container = builder.Build();

is working like a charm.

P.S. Some more info from Microsoft

Answer:

The problem is the main project is looking for the type you entered to the config.json file but it doesn’t have reference to the implementations project so it will not found the module. My solution is, first add a reference to the implementations project then create an autofac module in the implementations project (you need autofac lib in the implementations project so it is better to have a shared kernel or simply shared project for all projects):

public class ImplementationModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<ImplementationN>().As<InterfaceN>();
        }
    }

All your registration of the implimentation project goes here.
Then change the config.json file like this:

{
  "modules": [
    {
      "type": "Implementations.ImplementationModule, Implementations"
    }
  ]
}

Then everything should be fine.
If you want don’t want your main project to have reference to the implementations project you need to add some new project that to the registration of all the modules of your application. And it should have references to all the projects as well.