说我有两个由Castle Windsor实例化的类,每个类都依赖于同一个接口:
FooRepo
→IApiClient
BarRepo
→IApiClient
在这种情况下,IApiClient
由一个知道如何与任何API通信的类GenericApiClient
实现。但是,我想创建GenericApiClient
的不同实例,这些实例传递不同的配置值(通过IApiClientConfiguration
公开),以便FooRepo
与Foo API端点和BarRepo
会谈进行对话到Bar API端点:
FooRepo
→IApiClient (GenericApiClient)
→IApiClientConfiguration (FooClientConfiguration)
BarRepo
→IApiClient (GenericApiClient)
→IApiClientConfiguration (BarClientConfiguration)
这是我迄今为止所做的尝试:
container = new WindsorContainer();
container.Register(
Component.For<HomeController>().LifeStyle.Transient,
Component.For<FooRepo>()
.LifeStyle.Transient,
Component.For<BarRepo>()
//.DependsOn(Dependency.OnComponent<IApiClientConfiguration, BarClientConfiguration>()) // this does nothing cause the client config is not a direct dependency :(
.LifeStyle.Transient,
Component.For<IApiClient>()
.ImplementedBy<GenericApiClient>()
//.DependsOn(Dependency.OnComponent<IApiClientConfiguration, BarClientConfiguration>()) // this overrides for both FooRepo and BarRepo :(
.LifeStyle.Transient,
Component.For<IApiClientConfiguration>()
.ImplementedBy<FooClientConfiguration>()
.LifeStyle.Transient,
Component.For<IApiClientConfiguration>()
.ImplementedBy<BarClientConfiguration>()
.LifeStyle.Transient);
我无法弄清楚如何让FooRepo
获得GenericApiClient
配置FooClientConfiguration
的实例,BarRepo
获得BarClientConfiguration
}}:
FooClientConfiguration
,因为这是首先注册的IApiClient
覆盖DependsOn(...)
的配置,但这适用于FooRepo
和BarRepo
DependsOn(...)
上使用BarRepo
,则无效(如果上述问题不明确,我有一个最低工作示例here)
我有什么方法可以让Castle Windsor按照我的意愿去做吗?有没有办法更好地构建我的代码,所以我没有这个问题?
答案 0 :(得分:4)
container.Register(
Component.For<IApiClientConfiguration>()
.ImplementedBy<FooClientConfiguration>()
.Named("FooConfiguration")
.LifestyleTransient(),
Component.For<IApiClientConfiguration>()
.ImplementedBy<BarClientConfiguration>()
.Named("BarConfiguration")
.LifestyleTransient(),
Component.For<IApiClient, GenericApiClient>()
.Named("FooClient")
.DependsOn(Dependency.OnComponent(
typeof(IApiClientConfiguration), "FooConfiguration")),
Component.For<IApiClient, GenericApiClient>()
.Named("BarClient")
.DependsOn(Dependency.OnComponent(
typeof(IApiClientConfiguration), "BarConfiguration")),
Component.For<FooRepo>()
.DependsOn(Dependency.OnComponent(typeof(IApiClient), "FooClient")),
Component.For<BarRepo>()
.DependsOn(Dependency.OnComponent(typeof(IApiClient), "BarClient"))
);
这不漂亮。并且可能有一种方法可以简化语法。温莎通常提供一些不同的方式来做所有事情。
您正在定义GenericApiClient
的两个不同实现,并且每个实现都指定要使用的配置。然后,在注册FooRepo
和BarRepo
时,为每个人指定要使用的IApiClient
的命名实现。
如果一个或另一个是默认值,那么您可以使用IsDefault()
指示它并仅命名另一个。如果为不同的实现组编写单独的安装程序,或者甚至只是在同一安装程序中的方法,也可以更容易理解,例如
RegisterFooDependencies(IWindsorContainer container)
{
container.Register(
Component.For<IApiClientConfiguration>()
.ImplementedBy<FooClientConfiguration>()
.Named("FooConfiguration")
.LifestyleTransient(),
Component.For<IApiClient, GenericApiClient>()
.Named("FooClient")
.DependsOn(Dependency.OnComponent(
typeof(IApiClientConfiguration), "FooConfiguration")),
Component.For<FooRepo>()
.DependsOn(Dependency.OnComponent(typeof(IApiClient), "FooClient"))
);
}
答案 1 :(得分:1)
@ Scott-Hannen给出的答案技术上正确,当他说
时他也是正确的它不漂亮。
温莎非常灵活,你可以通过足够的配置和扩展来做很多事,但是你应该这样做吗?
通常,如果事情变得太复杂,你发现自己在向工具解释你的架构是如何组合在一起时遇到问题,那么解释和维护人类可能会过于复杂?
您有两组抽象,它们总是在一起(您的Repo
和IApiClientConfiguration
)由另一个抽象IApiClient
隔开。
如果它们齐头并进,我会将它们并排放置,并重新排列依赖关系链,以便最终得到类似的内容:
public FooRepo(IApiClient client, FooClientConfiguration clientConfig)
{
// ...stuff, and then
client.Configure(config);
}
现在,您正在使任何阅读代码的人明白且明显地建立关系。而且,作为一个附带好处,您的Windsor配置也将变得微不足道。
答案 2 :(得分:1)
我从@Krzysztof Kozmic那里读到了答案。我同意这一点 - 我接受的答案,虽然技术上正确,但需要一些复杂的配置。
我不确定我是否同意让一个类配置自己的依赖项。如果我们这样做:
public FooRepo(IApiClient client, FooClientConfiguration clientConfig)
{
// ...stuff, and then
client.Configure(config);
}
然后FooRepo
对IApiClient
了解太多。它知道它需要客户端配置。它不是一种抽象。我没有在最初的答案中指出这一点,但为什么FooRepo
甚至应该知道它依赖的是API客户端,更不用说需要配置的API客户端?
因此,虽然我的第一个答案是字面意思 - 如何让温莎做出被问到的问题 - 这是解决原始问题的更好方法:
首先,不要让FooRepo
或BarRepo
依赖于IApiClient
。他们不应该对API客户端有任何了解。他们只是依赖某种提供服务的东西,不知何故。也许是这样的:
public interface IFooService
然后实现如下:
public class FooApiClient : GenericApiClient, IFooService
{
public FooApiClient() : base(new FooClientConfiguration())
{}
// Whatever IFooService does
}
我厌倦了使用依赖注入执行所有。但在这种情况下,为了便于阅读,我们可以创建一个用给定配置组成API客户端的类。
FooApiClient
与FooClientConfiguration
耦合(我们通常试图通过DI避免这种情况),但这就是它的目的 - 提供耦合配置,使其不存在于任何一个DI配置或依赖它的类。
现在凌乱的东西被移出DI配置,FooRepo
甚至更简单,因为它取决于 true 抽象,而不是I
in它的前面但实际上代表了一个具体的实现(API客户端。)