使用 Autofac 查找插件

Gle*_*ten 5 c# autofac

花了很多时间弄清楚如何让 Autofac 加载插件。我是 Autofac 的新手。

我有像 ServiceA.dll、ServiceB.dll 等插件。在配置我的 Autofac 容器时,我扫描当前目录中的 Service*.dll 并从文件名和存储中提取服务“A”、“B”的名称在名为 serviceName 的变量中。然后我找到实现 IService 的服务类的类型并将其放入名为 serviceType 的变量中。每个服务还有一个名为 <serviceName>Settings 的配置设置类,因此我找到该类并将其类型放在名为 serviceSettingsType 的变量中。然后注册:

builder.RegisterType(serviceType)
  .Named<IService>(serviceName);
builder.RegisterType(serviceSettingsType)
  .Named<IServiceSettings>(serviceName+"Settings")
Run Code Online (Sandbox Code Playgroud)

构建 Autofac 配置后,我可以获得如下服务类型:

scope.ResolveNamed<IService>("A");
Run Code Online (Sandbox Code Playgroud)

并且很快,类 ServiceA 被实例化。但是,ServiceA 需要一个 ctor 参数:

public class ServiceA(ServiceASettings settings) {}
Run Code Online (Sandbox Code Playgroud)

Autofac 很好地实例化了一个 ServiceASettings 类(它只是一个具有一个或多个属性的“数据类”)并将其传递到 ServiceA 的构造函数中。

我无法弄清楚的是如何告诉 Autofac 创建一个 ServiceASettings 类以传递到 ServiceActor 它需要实例化一个 SettingsManager 类然后调用var settings = await Task<T> ReadFromFile(string settingsFilename)(其中 T 是 ASettings 类的类型)并使用该设置类在 ServiceA 的 ctor 调用中。就像上面的服务名称(示例中的“A”)是从命令行确定的一样,传递给 ReadFromFileAsync 的 settingsFilename 需要在运行时传入。

希望这是有道理的。我觉得我已经非常接近使用 Autofac 加载插件了,但就是无法为 Service ctor 找出正确的工厂调用或命名参数调用。有人用 Autofac 做过类似的事情吗?更改插件类和/或 SettingsManager 类架构的建议也非常受欢迎,如果它有助于使用 Autofac 使这项工作更容易。

编辑:

得到这个工作。我重新阅读了@cynic 指向的页面,最终为我点击了语法运行时参数。

将我的 SettingsManager 类更改为一个名为 SettingsFile 的类,它采用一个类型、一个 IFileSystem 和一个带有文件名的字符串并公开一个 ReadSettings 方法。登记:

builder.Register((c, p) => new SettingsFile(
    c.Resolve<IFileSystem>(),
    p.Named<Type>("settingsType"),
    p.Named<string>("settingsFilename")))
    .As<ISettingsFile>();
Run Code Online (Sandbox Code Playgroud)

解决:

var settingsName = "Service1Settigns";
var settingsType = scope.ResolveNamed<ISettings>(settingsName);
var settingsFile = scope.Resolve<ISettingsFile>(
    new NamedParameter("settingsType", settingsType.GetType()),
    new NamedParameter("settingsFilename", settingsFilename));
var settings = settingsFile.ReadSettings();
Run Code Online (Sandbox Code Playgroud)

(我认为有一种方法可以让解析器为我调用 ReadSettings,但还没有弄清楚。但无论如何,我可能想要捕获错误并使用非常具体的错误消息进行增强。)

然后在扫描插件 DLL 时,各种类被注册如下:

var assemblyTypes = assembly.GetTypes();
var settingsTypes = FindType(assemblyTypes, typeof(ISettings));
var settingsType = settingsTypes.First();
builder.RegisterType(settingsType).Named<ISettings>(settingsName);
Run Code Online (Sandbox Code Playgroud)

对 IService 和 IRepository 进行了类似的注册,以下是客户端的启动方式:

var client = scope.ResolveNamed<IClient>(clientName,
    new NamedParameter("settings", settings),
    new NamedParameter("repository", repository));
Run Code Online (Sandbox Code Playgroud)

这为我提供了一个基于 clientName 的 IClient,在这种情况下,它是在命令行上指定的(为此客户端使用的适当设置文件路径也是如此)。

开始真正喜欢 Autofac 的功能!