如何使用 Autofac 5 通过配置文件(运行时配置)使用外部插件

Howto use external plugins via config file (runtime configuration) with Autofac 5

我正在尝试设置一个简单的 .NET Core 3.1 项目,该项目使用 Autofac 5 IoC Container.

我添加了对最新 Autofac.Configuration 5.0.0 package.

的引用

我已经使用简单的 JSON 配置(遵循 this guide)创建了界面、实现和测试应用程序:

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

我使用以下代码(根据同一指南):

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();
------------------------

但我得到 System.InvalidOperationException

The type 'Implementations.ImplementationN, Implementations' could not be found. It may require assembly qualification, e.g. "MyType, MyAssembly"

我已将我的代码上传到 GitHub。感谢任何帮助。

P.S. Visual Studio 2019 16.4.5 企业版,Windows 10 1909 x64 专业版

更新: 澄清更多 - 我的最终目标是 Interfaces.dll 没有特殊参考,Implementations.dll 仅参考 Interfaces.dllTest.exe 参考只有 Interfaces.dll(当然还有 Autofac 包)。我希望 Autofac 通过反射从特定程序集(在 config.json 中指定)加载特定 class。这可以通过 Unity Container 实现,我希望通过 Autofac IoC.

实现同样的效果

再一次:我需要一个没有任何项目引用Implementations.dll的upproach,我应该能够改变具体的实现(通过改变config.json)没有重新编译。

问题是主项目正在查找您在 config.json 文件中输入的类型,但它没有对实现项目的引用,因此它不会找到模块。我的解决方案是,首先添加对实施项目的引用,然后在实施项目中创建一个 autofac 模块(实施项目中需要 autofac 库,因此最好为所有项目共享内核或简单地共享项目):

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

您所有的实施项目注册都在这里。 然后像这样更改 config.json 文件:

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

那应该就没问题了。
如果您不想让您的主项目引用实施项目,您需要将一些新项目添加到应用程序所有模块的注册中。它也应该有对所有项目的引用。

我发现这更像是一个“.NET Core 程序集加载”问题,而不是 Autofac 问题。

长话短说,如果程序集没有被您的应用程序专门引用,您需要告诉 .NET Core 程序集加载程序从哪里获取它即使它在您的 BIN 文件夹中.

因此,如果您(像我一样)不想拥有引用您需要的 所有 实现库的 bootstrap 库,但您想要运行时配置通过配置文件 - 您应该执行以下操作。

来自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.
// 
//
// 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"));
 };

然后是以下代码:

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

工作得很好。

P.S. 来自 Microsoft

的更多信息