Autofac 的 JSON 配置中同一接口的两种实现

Two implementations of the same interface in Autofac's JSON configuration

我需要在应用程序中启用后期绑定,我希望有一个选项可以使用 JSON 配置文件显式配置服务。

我有一个接口 IDependency 和两个 classes DependencyOneDependencyTwo 它们都实现了接口。我还有一个 SomeService class 有一个带有以下签名的构造函数:

public SomeService(IDependency dependency1, IDependency dependency2)

我想为 dependency1 注入 DependencyOne,为 dependency2 注入 DependencyTwo

如何只在 JSON 配置中配置它,而不在代码中使用任何属性?有可能吗?

需要无属性要求,因为后期绑定程序集不应依赖于 AutoFac。

提前致谢。

更新:解决方案

Travis 的以下回答包含 link 常见问题解答,这使我找到了可接受的解决方案。使用 "marker" 接口,例如IDependencyOne : IDependencyIDependencyTwo : IDependency,然后是 SomeService(IDependencyOne dependency1, IDependencyTwo dependency2)。我不太喜欢的是现在 IDependency 的通用装饰器需要实现所有标记,比如 LoggingDecorator : IDependencyOne, IDependencyTwo 如果我想在 SomeService 中使用它,但只要标记保持空白,这不是大问题。这样我就不必在后期绑定程序集中强制依赖 Autofac 的 dll,同时仍然在 JSON 文件中配置了 DI。

如果同一接口有两种不同的实现,但不能一视同仁,那就是代码异味。 There is an FAQ about ways to work around that 但简短的回答是 如果不进行一些手动操作,您实际上无法像这样指定同一接口的两个不同实例。 没有一种机制可以很容易连接起来,因为这是一个设计问题。

花点时间阅读 the FAQ 以了解原因以及解决此问题的其他想法,而不仅仅是我在这里展示的内容。

但是,假设您无法更改界面 IDependency,因为通常这是人们的症结所在。我们还假设您不能出于任何原因将 DependencyOneDependencyTwo 直接放在构造函数中。 (这两个都是我首先要解决的问题,而不是试图使我的 DI 连接复杂化,但再次声明,为了争论,这不是一个选项。)

我可能会在配置中为每个实例注册一个元数据键,以后可以使用。

{
  "components": [{
    "type": "MyAssembly.DependencyOne, MyAssembly",
    "services": [{
      "type": "MyAssembly.IDependency, MyAssembly"
    }],
    "metadata": [{
      "key": "type",
      "value": "One",
      "type": "System.String, mscorlib"
    }]
  }, {
    "type": "MyAssembly.DependencyTwo, MyAssembly",
    "services": [{
      "type": "MyAssembly.IDependency, MyAssembly"
    }],
    "metadata": [{
      "key": "type",
      "value": "Two",
      "type": "System.String, mscorlib"
    }]
  }]
}

好的,所以我们有两个组件,每个组件都公开相同的接口,每个组件的元数据键分别为 OneTwo

In your class you can use that metadata.

public class SomeService
{
  readonly IEnumerable<Meta<IDependency>> _dependencies;

  public SomeService(IEnumerable<Meta<IDependency>> dependencies)
  {
    _dependencies = dependencies;
  }

  public void DoSomething(string parameter)
  {
    var dep = _dependencies.First(a => a.Metadata["type"].Equals(parameter));
    dep.DoSomething();
  }
}

想法是您可以使用元数据选择合适的东西。显然要根据您自己的需要进行调整;也许它不是来自调用者的参数,而是您代码中其他地方的参数;这个概念仍然成立。

不过,我再次强烈建议您 check out the FAQ 强烈考虑重新设计以完全避免这种情况 。在长期 运行.

中,这将使您和您的开发人员的生活更轻松