4

Thanks to my new job, I recently discovered the inversion of control design principle (with windsor castle in C#).

I really enjoy using it but something's bothering me. For me the good part about strongly typed languages is the fact that you can see errors at compile time and you avoid having your app randomly crashing while running just because you made a typo on a dependency name. But inversion of control cancel all this, if you have a missing dependency there will be no problem at compile-time.

Is there a way to tell the compiler to check if the dependencies really exist ?

  • As a compromise it might be possible to do the checking at load time instead of when the IoC container tries to instantiate the concrete type. – CodesInChaos May 31 '16 at 7:49
2

Alternative solution:
If your container is able to validate its own configuration, you can write an integration test for that.

That's not really compile time checking, but if it's part of your test suite, you'll still notice missing dependencies quickly.
Of course it only works when all your dependencies are known at compile time.


Here's an example from a recent project of mine, which uses SimpleInjector.
SimpleInjector has a Verify() method, which tries to resolve all registered dependencies.

I'm using SimpleInjector like this:

public class Bootstrapper
{
    public static Container BuildContainer()
    {
        var container = new Container();
        container.Register<IFoo, Foo>();
        container.Register<IBar, Bar>();
        // etc.

        container.Verify();

        return container;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var container = Bootstrapper.BuildContainer();

        container.GetInstance<IFoo>().Run();
    }
}

Verify will throw an exception when anything goes wrong, so the app would crash at runtime.

To check this while running my test suite, I wrote the following test (xUnit.net syntax):

public class BootstrapperTests
{
    [Fact]
    public void DependenciesWereResolved()
    {
        bool error = false;

        try
        {
            var container = Bootstrapper.BuildContainer();   
        }
        catch (InvalidOperationException)
        {
            error = true;
        }

        Assert.False(error);
    }
}

Note: xUnit.net doesn't have anything like Assert.DidNotThrow<Exception>, that's why I needed to catch the exception and set the error variable.

  • But what about cases where a class is Resolved by -but not Registered in- the IoC container and one of its dependencies is missing? Automated tests would only pick that up if you test the Resolution of all types in your application, or, if you did some static analysis to find out which types could be subject to Resolution... which starts smelling like the halting problem. – MetaFight May 30 '16 at 18:37
  • @MetaFight: I know that it won't work in all cases, maybe I should have said that clearer in my answer. But then...SimpleInjector's Verify() does "iterate over all registrations and resolve an instance for each registration" (quote from the docs), so if any of those types' dependencies are missing, my approach would catch that. Can you give an example where you think my approach wouldn't work? – Christian Specht May 30 '16 at 19:01
  • Your approach only considers registered types. It won't cover cases where the dev uses the IoC container to resolve classes completely disconnected from the registered class graph. Eg, IoC.Register<A>(); IoC.Register<B>(); var z = IoC.Resolve<Z>(). Z hasn't been registered therefore your test doesn't check if it resolves correctly. So, if Z has dependency Y, which also isn't registered, we only see this at runtime. – MetaFight May 30 '16 at 20:48
  • I've never seen someone do that...but then again, I'm not that experienced with IoC. So for my use cases, when I know that I'll never try to resolve something I didn't register before, my approach does the job. – Christian Specht May 30 '16 at 21:06
5

If you'd rather keep compile time checking, there's absolutely no reason why you couldn't build your dependencies using so-called "poor man's dependency injection" - that is to say, building the dependency tree yourself by calling constructors. Though DI containers are a powerful tool that can support Inversion Of Control in code, they are not the only way to invert your dependencies. The key salient point here is that each unit of code still relies on abstracted interfaces that are injected at resolution time, and not on other concrete classes.

If you do take this route, you may still want to extract out the dependency resolution route. That is to say, instead of making building the application stack the responsibility of the controllers in an MVC application, it would be preferable to build the application and data layer in an external factory, and inject those dependencies into the controllers as required. This way, you have a code component that could potentially manage the lifetime as well as construction of your objects.

Of course, at this point, you're ironically working towards a sort of basic IoC container, but it's an IoC container that's highly specific to your application. For a simpler application, this may well be preferable to a full-blown DI Container, with registration-by-convention and all the bells-and-whistles.

  • You hit all the major points. Nice. – MetaFight May 30 '16 at 18:33
3

Not for most IOC containers.

I mean, the usual purpose of them is to gather plugins and tie everything together. It rather defeats the purpose if all of your plugins are known at compile time.

And moving compile time errors to runtime errors is one of the significant drawbacks to these things. They are decidedly not universally good.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.