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
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.
container.AddNewExtension<Interception>();
container.RegisterType<IThing>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<DecoratingThingBehavior>());
and then it would be "inject this IThing
into this and that Depender
"
container.RegisterType<Depender1>(new InjectionConstructor(new ResolvedParameter<IThing>("myNameForThing")));
The class design itself seems reasonable. Here's a convention-based container configuration that basically does this:
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)));
}
}
}
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
or Depender5
) without revisiting the container configuration at all.
The above configuration satisfies these unit tests:
[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);
}