Autofac 的 JSON 配置中同一接口的两种实现
Two implementations of the same interface in Autofac's JSON configuration
我需要在应用程序中启用后期绑定,我希望有一个选项可以使用 JSON 配置文件显式配置服务。
我有一个接口 IDependency
和两个 classes DependencyOne
和 DependencyTwo
它们都实现了接口。我还有一个 SomeService
class 有一个带有以下签名的构造函数:
public SomeService(IDependency dependency1, IDependency dependency2)
我想为 dependency1
注入 DependencyOne
,为 dependency2
注入 DependencyTwo
。
如何只在 JSON 配置中配置它,而不在代码中使用任何属性?有可能吗?
需要无属性要求,因为后期绑定程序集不应依赖于 AutoFac。
提前致谢。
更新:解决方案
Travis 的以下回答包含 link 常见问题解答,这使我找到了可接受的解决方案。使用 "marker" 接口,例如IDependencyOne : IDependency
和 IDependencyTwo : 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
,因为通常这是人们的症结所在。我们还假设您不能出于任何原因将 DependencyOne
和 DependencyTwo
直接放在构造函数中。 (这两个都是我首先要解决的问题,而不是试图使我的 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"
}]
}]
}
好的,所以我们有两个组件,每个组件都公开相同的接口,每个组件的元数据键分别为 One
或 Two
。
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 和 强烈考虑重新设计以完全避免这种情况 。在长期 运行.
中,这将使您和您的开发人员的生活更轻松
我需要在应用程序中启用后期绑定,我希望有一个选项可以使用 JSON 配置文件显式配置服务。
我有一个接口 IDependency
和两个 classes DependencyOne
和 DependencyTwo
它们都实现了接口。我还有一个 SomeService
class 有一个带有以下签名的构造函数:
public SomeService(IDependency dependency1, IDependency dependency2)
我想为 dependency1
注入 DependencyOne
,为 dependency2
注入 DependencyTwo
。
如何只在 JSON 配置中配置它,而不在代码中使用任何属性?有可能吗?
需要无属性要求,因为后期绑定程序集不应依赖于 AutoFac。
提前致谢。
更新:解决方案
Travis 的以下回答包含 link 常见问题解答,这使我找到了可接受的解决方案。使用 "marker" 接口,例如IDependencyOne : IDependency
和 IDependencyTwo : 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
,因为通常这是人们的症结所在。我们还假设您不能出于任何原因将 DependencyOne
和 DependencyTwo
直接放在构造函数中。 (这两个都是我首先要解决的问题,而不是试图使我的 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"
}]
}]
}
好的,所以我们有两个组件,每个组件都公开相同的接口,每个组件的元数据键分别为 One
或 Two
。
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 和 强烈考虑重新设计以完全避免这种情况 。在长期 运行.
中,这将使您和您的开发人员的生活更轻松