Generic dependency in a non-generic class

Oct 17, 2014 at 6:42 PM
Edited Oct 17, 2014 at 6:51 PM
I have come across a case where I have an "open generic dependency" (which is closed by a generic type parameter on a method) within a class. What would you recommend as to the best pattern for resolving this dependency?
public interface IDependency<T>
{
    void DoIt();
}

internal class Dependency<T> : IDependency<T>
{
    void IDependency<T>.DoIt()
    {
        //do something
    }
}

internal class Parent
{
    public void DoSomethingOnThisType<T>()
    {
        //dependency.DoIt();
    }
}
One way I came up with was to make a non-generic factory for IDependency as follows:
public interface IDependencyFactory
{
    CreateDependency<T>();
}

internal class DependencyFactory : IDependencyFactory
{
    IDependency<T> IDependencyFactory.CreateDependency<T>()
    {
        return new Dependency<T>();
    }
}

public interface IDependency<T>
{
    void DoIt();
}

internal class Dependency<T> : IDependency<T>
{
    void IDependency<T>.DoIt()
    {
        //do something
    }
}

internal class Parent
{
    private readonly IDependencyFactory dependencyFactory;

    public Parent(IDependencyFactory dependencyFactory)
    {
        this.dependencyFactory = dependencyFactory;
    }

    public void DoSomethingOnThisType<T>()
    {
        IDependency<T> dependency = this.dependencyFactory.CreateDependency<T>();
        dependency.DoIt();
    }
}
However, this can become sloppy very quickly if Dependency<T> has dependencies which also use T. The reason for this is that I would be injecting all dependencies of Dependency<T> into DependencyFactory so that there are no container.GetInstance<>() calls and container.Verify() is able to build the entire object graph. Thus, I would need to inject non-generic factories for all of the dependencies of Dependency<T> as well:
public interface INonGenericDependency { }

public interface IAnotherDependencyFactory
{
    CreateAnotherDependency<T>();
}

internal class AnotherDependencyFactory : IAnotherDependencyFactory
{
    IAnotherDependency<T> IAnotherDependencyFactory.CreateAnotherDependency<T>()
    {
        return new AnotherDependency<T>();
    }
}

public interface IAnotherDependency<T>
{
    void DoIt();
}

internal class AnotherDependency<T> : IAnotherDependency<T>
{
    void IAnotherDependency<T>.DoIt()
    {
        //do something
    }
}

public interface IDependencyFactory
{
    CreateDependency<T>();
}

internal class DependencyFactory : IDependencyFactory
{
    private readonly INonGenericDependency nonGenericDependency;
    private readonly IAnotherDependencyFactory anotherDependencyFactory;

    public DependencyFactory(
        INonGenericDependency nonGenericDependency,
        IAnotherDependencyFactory anotherDependencyFactory)
    {
        this.nonGenericDependency = nonGenericDependency;
        this.anotherDependencyFactory = anotherDependencyFactory;
    }

    IDependency<T> IDependencyFactory.CreateDependency<T>()
    {
        return new Dependency<T>(this.nonGenericDependency, this.anotherDependencyFactory.CreateAnotherDependency<T>());
    }
}

public interface IDependency<T>
{
    void DoIt();
}

internal class Dependency<T> : IDependency<T>
{
    private readonly INonGenericDependency nonGenericDependency;
    private readonly IAnotherDependency<T> anotherDependency;

    public Dependency<T>(
        INonGenericDependency nonGenericDependency,
        IAnotherDependency<T> anotherDependency)
    {
        this.nonGenericDependency = nonGenericDependency;
        this.anotherDependency = anotherDependency;
    }

    void IDependency<T>.DoIt()
    {
        //do something
    }
}

internal class Parent
{
    private readonly IDependencyFactory dependencyFactory;

    public Parent(IDependencyFactory dependencyFactory)
    {
        this.dependencyFactory = dependencyFactory;
    }

    public void DoSomethingOnThisType<T>()
    {
        IDependency<T> dependency = this.dependencyFactory.CreateDependency<T>();
        dependency.DoIt();
    }
}
I realize that this may be necessary as container.Verify() must be able to build the entire object graph, and there may be no way for it to be able to "fill in" generic types arbitrarily during the Verify stage, but it seems like my solution could spiral into a huge mess, so I wanted to see if I am overlooking something or attacking this the wrong way.
Coordinator
Oct 18, 2014 at 9:19 AM
Edited Oct 20, 2014 at 8:21 AM
It's a good think that you try to keep your configuration verifiable and I think you're on the right track with the factory. The simple fact is that you can't inject IDependency<T> into the parent's constructor, because there are an endless number of closed IDependency<T> versions that you might need to inject. So there is no other option than breaking the dependency graph by introducing a factory. But since you are breaking the dependency graph anyway, it's not a problem to let the factory call back into the container (as long as the factory implementation itself is part of your composition root).

The your factory should look like this:
private sealed class DependencyFactory : IDependencyFactory
{
    private readonly Container container;

    public DependencyFactory(Container container) 
    {
         this.container = container;
    }

    IDependency<T> IDependencyFactory.CreateDependency<T>()
    {
        return this.container.GetInstance<IDependency<T>>();
    }
}
Now your configuration might look like this:
container.Register<Parent>();
container.RegisterSingle<IDependencyFactory, DependencyFactory>();
container.RegisterOpenGeneric(typeof(IDependency<>), typeof(Dependency<>));
From a verifiability standpoint you are actually in quite a good place here, because verify could verify almost everything. But since the dependency graph is thorn between Parent and Dependency<T>, and Dependency<T> is open-generic registration (that uses unregistered type resolution to resolve closed instances of Dependency<T>), from Simple Injector's point of view there are more root objects now. Of course Parent is a root (unless someone else is depending on it of course). Dependency<T> is a root object as well, but Simple Injector doesn't know it exists. So Dependency<T>'s own dependencies now becomes root objects of their own (from Simple Injector's point of view). This means that when Verify() is called, both Parent and Dependency<T>'s own dependencies (with their graphs) are verified.

But the problem now is of course that Dependency<T> might have some dependency that isn't registered. This means that resolution could still fail, when Parent.DoSomethingOnThisType<T>() is called, and we don't want that. A simple workaround for this is to at least register one closed version of Dependency<T> explicitly:
container.Register<Parent>();
container.RegisterSingle<IDependencyFactory, DependencyFactory>();
container.RegisterOpenGeneric(typeof(IDependency<>), typeof(Dependency<>));

// Register explicitly for verifiability
container.Register<IDependency<SomeMessage>, Dependency<SomeMessage>>();
This extra registration lets Simple Injector know about the existence of Dependency<T> and this means that Dependency<SomeMessage> now becomes a root object of its own graph, and when Verify() is called, Simple Injector will try to create Dependency<SomeMessage> with its dependencies.

As long Dependency<T> doesn't reference other open-generic types, you'll be fine and adding that single Dependency<SomeMessage> registration will allow you to verify the object graphs with confidence.

In the case that there are (or could be) open-generic dependencies in there, it gets different. In that case you might want to make an explicit closed registration for that dependency as well. Or.. you might want to let go completely of the use of RegisterOpenGeneric for your Dependency<T>, and fallback to batch registration, where you determine all possible types that could be filled in in T and makes each registration explicitly. Usually there's a very specific set of types that are known compile-time and this allows you to go look for them using reflection and loop through them to make each registration explicitly.

But the moral of the story is that RegisterOpenGeneric uses unregistered type resolution and this takes special care if that type you register is a root type, because Simple Injector will not be able to verify it for you.
Marked as answer by dot_NET_Junkie on 10/20/2014 at 1:24 AM