Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring Unity to resolve a type that takes a decorated dependency that has a parameter that varies with the type into which it is injected

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?

like image 935
James World Avatar asked Jan 25 '12 14:01

James World


People also ask

What is Unity dependency injection?

Dependency Injection (DI) Dependency Injection is defined as a design pattern that allows removing hard-coded dependencies from an application. There is one major point to remember: “Inversion of control is principal and Dependency Injection is implementation”.

What is injection dependency C#?

Dependency injection in . NET is a built-in part of the framework, along with configuration, logging, and the options pattern. A dependency is an object that another object depends on. Examine the following MessageWriter class with a Write method that other classes depend on: C# Copy.

What is dependency injection stack overflow?

Dependency injection is a pattern to allow your application to inject objects on the fly to classes that need them, without forcing those classes to be responsible for those objects. It allows your code to be more loosely coupled, and Entity Framework Core plugs in to this same system of services.

What is Injectionfactory?

A class that lets you specify a factory method the container will use to create the object.


2 Answers

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);
}
like image 78
Mark Seemann Avatar answered Oct 18 '22 12:10

Mark Seemann


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")));
like image 39
Sebastian Weber Avatar answered Oct 18 '22 13:10

Sebastian Weber