配置 Unity 来解析采用修饰依赖项的类型,该依赖项的参数随注入的类型而变化
这是一个相当简单的装饰器模式场景,但复杂之处在于装饰类型具有一个构造函数参数,该参数依赖于要注入的类型。
我有一个像这样的接口:
interface IThing
{
void Do();
}
和一个像这样的实现:
class RealThing : IThing
{
public RealThing(string configuration)
{
... implementation ...
}
public void Do()
{
... implementation ...
}
}
和一个这样的装饰器:
class DecoratingThing : IThing
{
IThing _innerThing;
public DecoratingThing(IThing thing)
{
_innerThing = thing;
}
public void Do()
{
_innerThing.Do();
}
}
最后,我有一些需要 IThing
的类型,称为 Depender1
,Depender2
等。
class DependerX()
{
public DependerX(IThing thing)
{
... implementation ...
}
}
我想配置一个 IOC 容器来解析 DependerX
的实例,以便它们注入用 DecoratingThing
装饰的 RealThing
。代码>. 重要提示:每个 DependerX
类型 都需要将不同的 configuration
值传递给其 的构造函数RealThing
,在每种情况下都说“ConfigX”。例如,IoC 容器完成的工作可能是:
new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));
...等等。
在 Unity 中,这看起来配置起来相当笨重,因为我必须将装饰器与装饰器混合在一起:
container.RegisterType<IThing, DecoratingThing>("ConfigX",
new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));
container.RegisterType<DependerX>(
new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");
并且对于每个 DependerX 重复一遍,很好地违反了 DRY。
我想要做的是消除在 ITing
的每个命名注册中的 DecoratingThing
构造中嵌入 RealThing
构造的需要 -并且只声明一次装饰。例如,如果将来装饰需要改变,则更容易重新配置。我想出的最好的方法是这个注册辅助方法:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(new RealThing(config))));
}
这至少消除了重复,但我仍然必须在 DecoratingThing
中嵌入 RealThing
的构造 - 这意味着例如,我无法独立改变它们的寿命。我无法再次注册 IThing
来执行此操作,因为我已经用完该名称接口的注册。如果我想这样做,我必须引入另一组命名实例,如下所示:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
string realConfig = "Real" + config;
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(
container.Resolve<IThing>(realConfig))));
container.RegisterType<IThing, RealThing>(realConfig,
new ContainerControlledLifetimeManager(),
new InjectionConstructor(config));
}
这真的是最好的选择吗?对于那些后来的人来说,这感觉很复杂,而且可能很困难。其他 IoC 容器是否有令人信服的方法来覆盖这种场景?由于每个 DependerX 都会重复注入的工作模式,因此是否有办法仅在顶部 (DependerX
) 级别使用命名实例?
还有其他评论吗?
This is a fairly straight forward decorator pattern scenario, with the complication that the decorated type has a constructor parameter that is dependent on the type into which it is being injected.
I have an interface like this:
interface IThing
{
void Do();
}
And an implementation like this:
class RealThing : IThing
{
public RealThing(string configuration)
{
... implementation ...
}
public void Do()
{
... implementation ...
}
}
And a decorator like this:
class DecoratingThing : IThing
{
IThing _innerThing;
public DecoratingThing(IThing thing)
{
_innerThing = thing;
}
public void Do()
{
_innerThing.Do();
}
}
Finally, I have some types that require an IThing
, called Depender1
, Depender2
etc..
class DependerX()
{
public DependerX(IThing thing)
{
... implementation ...
}
}
I want to configure an IOC container to resolve instances of DependerX
such that they are injected with RealThing
decorated with a DecoratingThing
. Important: Each DependerX
type requires a different value of configuration
to be passed to the constructor of its RealThing
, say "ConfigX" in each case. e.g. The work done by the IoC container might be:
new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));
... and so on.
In Unity, this seems quite clunky to configure as I have to mix in the decorator with the decorated:
container.RegisterType<IThing, DecoratingThing>("ConfigX",
new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));
container.RegisterType<DependerX>(
new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");
And repeat, violating DRY nicely, for each DependerX
.
What I'd like to do is remove the need to embed the construction of RealThing
in the construction of DecoratingThing
in each named registration of IThing
- and declare the decoration just once. This is so, for example, that if the decoration needs to change in future, it's easier to reconfigure. The best I came up with is this helper method for registration:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(new RealThing(config))));
}
This removes repetition at least, but I still have to embed the construction of the RealThing
inside the DecoratingThing
- this means I can't vary their lifetimes independently for example. I can't register IThing
again to do this because I've used up my registration of that interface for the name. If I want to do that I have to introduce another set of named instances like so:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
string realConfig = "Real" + config;
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(
container.Resolve<IThing>(realConfig))));
container.RegisterType<IThing, RealThing>(realConfig,
new ContainerControlledLifetimeManager(),
new InjectionConstructor(config));
}
Is this really the best option? It feels complex and potentially hard for those that will come after to grok. Do other IoC containers have a compelling way to cover this scenario? Since the pattern for how injection works is repeated for each DependerX, is there a way to only use a named instance at the top (DependerX
) level?
Any other comments?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
类设计本身看起来很合理。这是一个基于约定的容器配置,基本上可以做到这一点:
此配置将自动添加与上述谓词匹配的所有类,因此一旦设置完毕,您就可以添加更多类(例如 < code>Depender4 或
Depender5
),而无需重新访问容器配置。上述配置满足这些单元测试:
The class design itself seems reasonable. Here's a convention-based container configuration that basically does this:
This configuration will automatically add all classes that match the above predicate, so once you've set it up, you can just add more classes (like
Depender4
orDepender5
) without revisiting the container configuration at all.The above configuration satisfies these unit tests:
您是否考虑过将您的装饰器基于 Unity 拦截功能?然后,“使用此拦截器拦截对
ITing
的调用”就非常容易了。然后它会是“将这个
IThing
注入到这个和那个Depender
中”Have you ever thought about basing your decorators on the Unity Interception functionality? Then it would be really easy to say "intercept the calls to
IThing
using this Interceptor" just once.and then it would be "inject this
IThing
into this and thatDepender
"