Jam*_*rld 12 c# dependency-injection ioc-container inversion-of-control unity-container
这是一个相当直接的装饰器模式场景,其复杂性在于,装饰类型具有一个构造函数参数,该参数取决于它被注入的类型.
我有这样的界面:
interface IThing
{
void Do();
}
Run Code Online (Sandbox Code Playgroud)
这样的实现:
class RealThing : IThing
{
public RealThing(string configuration)
{
... implementation ...
}
public void Do()
{
... implementation ...
}
}
Run Code Online (Sandbox Code Playgroud)
像这样的装饰者:
class DecoratingThing : IThing
{
IThing _innerThing;
public DecoratingThing(IThing thing)
{
_innerThing = thing;
}
public void Do()
{
_innerThing.Do();
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我有一些类型的需要IThing,叫Depender1,Depender2等.
class DependerX()
{
public DependerX(IThing thing)
{
... implementation ...
}
}
Run Code Online (Sandbox Code Playgroud)
我想配置一个IOC容器来解析DependerX它们被注入RealThing装饰的实例DecoratingThing.重要提示:每种DependerX 类型都需要configuration传递给它的构造函数的不同值,RealThing在每种情况下都要说"ConfigX".例如,IoC容器完成的工作可能是:
new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));
Run Code Online (Sandbox Code Playgroud)
... 等等.
在Unity中,这似乎很笨重的配置,因为我必须在装饰器中混合装饰:
container.RegisterType<IThing, DecoratingThing>("ConfigX",
new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));
container.RegisterType<DependerX>(
new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");
Run Code Online (Sandbox Code Playgroud)
并且重复一遍,很好地违反了DRY DependerX.
我想要做的是去除需要嵌入的建设RealThing在建设DecoratingThing中的每个命名登记IThing-并宣布装修一次.例如,如果装饰需要在将来更改,则更容易重新配置.我想出的最好的是这种注册帮助方法:
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))));
}
Run Code Online (Sandbox Code Playgroud)
这至少消除了重复,但我仍然需要嵌入RealThing内部的构造DecoratingThing- 这意味着我不能独立地改变他们的生命周期.我无法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));
}
Run Code Online (Sandbox Code Playgroud)
这真的是最好的选择吗?感觉很复杂,并且对于那些将要追求的人来说可能很难.其他IoC容器是否有令人信服的方式来涵盖这种情况?由于每个DependerX重复注入工作的模式,有没有办法只在top(DependerX)级别使用命名实例?
还有其他意见吗?
课堂设计本身似乎很合理.这是一个基于约定的容器配置,基本上这样做:
public class MyConventions : UnityContainerExtension
{
protected override void Initialize()
{
var dependers = from t in typeof(IThing).Assembly.GetExportedTypes()
where t.Name.StartsWith("Depender")
select t;
foreach (var t in dependers)
{
var number = t.Name.TrimStart("Depender".ToArray());
var realName = "Real" + number;
var decoName = "Deco" + number;
var config = "Config" + number;
this.Container.RegisterType<IThing, RealThing>(realName,
new InjectionConstructor(config));
this.Container.RegisterType<IThing, DecoratingThing>(decoName,
new InjectionConstructor(
new ResolvedParameter<IThing>(realName)));
this.Container.RegisterType(t,
new InjectionConstructor(
new ResolvedParameter<IThing>(decoName)));
}
}
}
Run Code Online (Sandbox Code Playgroud)
此配置将自动添加与上述谓词匹配的所有类,因此一旦设置完毕,您可以添加更多类(如Depender4或Depender5),而无需重新访问容器配置.
以上配置满足以下单元测试:
[Fact]
public void ContainerCorrectlyResolvesDepender1()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender1>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config1", thing.Configuration);
}
[Fact]
public void ContainerCorrectlyResolvesDepender2()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender2>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config2", thing.Configuration);
}
[Fact]
public void ContainerCorrectlyResolvesDepender3()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender3>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config3", thing.Configuration);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3176 次 |
| 最近记录: |