Ninject Tips: Some Binding Magic & Kernel Cleanup

Emptiness: The Magic Trick, magician & assistant, top hat and cape, painted panels, magic box, red, black, blue, white, painting, Seatac Airport, Seattle, Washington, USA

I’ve been happily using the Ninject Dependency injection library on my C# project for over a year.

In order to keep my project even reasonably organized, I quickly started using Modules to group together my dependencies. For the most part in my project, my Modules correlate to the first layer of namespaces beneath the project root. Additionally, because of the way I organize my classes (adhering to the Single Responsibility Principle (SRP) and highly compose-able), many of my Modules consisted primarily of stateless SRP classes, whose only injected constructor arguments are other stateless SRP classes. These classes are essentially stateless, and should therefore have singleton implementations. In addition, to make mocking easier, most of them are the sole implementation of an interface.

Conceptually, this is all sound, but it didn’t take long until typing this got really old:

public override void Load() 
{
    Bind.To.InSingletonScope();
    Bind.To.InSingletonScope();
    // ... many more of the same ...
}

A Little Binding Magic

So let’s DRY this up, shall we? How about for a specific Module, we load up all the classes in the namespace and bind them to interfaces?

public override void Load() 
{
    this.Bind(x => x
        .From(GetType().Assembly)
        .SelectAllClasses().InNamespaceOf(GetType())
        .BindAllInterfaces()
        .Configure(b => b.InSingletonScope()));
}

Occasionally I also have classes that don’t implement and interface but that I still want to treat as singletons. Generally these are public classes with no parent class, so I added this bit of logic:

this.Bind(x => x
    .From(GetType().Assembly)
    .SelectAllClasses().InNamespaceOf(GetType())
    .Where(t => t.BaseType == typeof (object) // no parent class
                && t.GetInterfaces().Length == 0 // no interfaces
                && t.DeclaringType == null) // not an inner class
    .BindToSelf()
    .Configure(b => b.InSingletonScope()));

Every now and then I would need a bit more flexibility, so I added the ability to exclude specific classes and to include additional namespaces (for example, to handle classes in other assemblies). The pattern shared by both my above examples ends up looking like this:

public virtual IList Excluding
{
    get { return new List(); }
}

public virtual IList Namespaces
{
    get { return new List {GetType().Namespace}; }
}

public override void Load() 
{
    this.Bind(x => x
        .From(GetType().Assembly)
        .SelectAllClasses().InNamespaces(Namespaces)
        .Excluding(Excluding)
   // now do binding
}

Now when I create a new class conforming to these patterns, I don’t have to worry about setting it up for dependency injection at all — it is done automatically. For example, my original two bindings of ISomeClassOrOther and IYetAnotherClass would be bound to their implementations.

Kernel Cleanup

This was all well and good, but later I noticed that my system tests were leaking memory like a sieve. I wanted to reset the Kernel (the source that provides dependency implementations) between each test run, but my naive implementation of just removing all my references to the old Kernel and instantiating a new one was far from sufficient. When I used some memory profiling tools, I discovered that components in the new Kernels ended up referencing the original Kernel.

Pretty quickly I decided the only safe thing to do would be to maintain the same kernel, but reset all its components. After doing a bit of digging, it turns out that is as simple as:

kernel.GetModules()
      .Where(m => m is MyNinjectModule) // only for modules I created
      .ForEach(m => kernel.Unload(m.Name));
kernel.Components.Get().Clear();

Then I just needed to tell the Kernel to load all my Modules again the same way I did the first time.

On later searching, I found the MockingKernel extension, which would have helped with some of the things I need, but not enough. It resets the cache but doesn’t clear the bindings. So I would still need to reset the bindings manually, or I would need to instantiate a new Kernel. And after what I’ve seen, I wasn’t going anywhere near that.

Don’t Let the Perfect Be the Enemy of the Good

I’ve found Ninject to be very helpful, but also occasionally obtuse. It has a lot of interesting and powerful features for which I’ve barely scratched the surface. On the other hand, even in what I’ve seen, I’ve found it necessary to be careful how I use it. Though I can’t really fault Ninject for that — no library is perfect.

If you ever do find a library that is both incredibly useful and without flaws, let me know. Or better yet, don’t tell anyone and invest all your money in the library’s authors.
 

Conversation
  • Anonymous says:

    Your default binding should always be InTransientScope() until you have a reason not too. In my experience, it’s very rare that a class should require singleton scope! Other readers, beware this sample code is not the recommended pattern.

    I don’t believe that your objects are so expensive to create, or are all perfectly stateless, that this actually makes a difference to your program speed or memory footprint. All the singleton binding is likely the source of your memory issues (NInject is honouring your request to _always_ return the same instance)

    I’m unsure about your comments regarding tests. Unless you’re performing integration tests, you shouldn’t even be using DI or creating any bindings. When testing an object, you don’t test the objects dependencies. Use a library like Moq. It’s quite straigthforward.

    The point of the manual binding in DI is meant to make it explicit, remove the magic, and expose the dependencies. IMO this kind of code is only temporarily satisfying, but ultimately will cause you discomfort down the road when you’re forced to just type it normally.

    Instead of all the reflection and assembly scanning *magic*, you could at least not paint yourself into a corner with the following kind of quasi-macro code.

    BasicBind();
    BasicBind();
    SingletonBasicBind();

    // create BasicBind and SingletonBasicBind to match type based on interface name.

    see NInject Object Scopes

    • Will Pleasant-Ryan Will Pleasant-Ryan says:

      Hi. Thanks for your comments. It looks like maybe I did not sufficiently explain a few things about what I am trying to do with Ninject.

      The code in this post is designed to simplify a specific type of binding which is common in my project; it is not intended to be a catch-all. I only applied it to specific IModule’s in my project, which generally speaking corresponded to a specific namespace. For the modules that use this pattern, every class in the module conformed to what I have referred to as a “stateless single-responsibility principle” class. I plan to go into more detail about the underlying pattern in a later post, but basically my project has a relatively small number of stateful classes which are relatively simple in nature, and a large number of stateless classes that create or modify the stateful ones. My stateful classes generally live in different namespaces than the stateless ones, and this pattern was not applied to those namespaces. In those cases often the standard binding techniques are used. So I haven’t painted myself into a corner, because the pattern is not universally applied. Moreover, I made it easy for individual classes to “opt” out, as it were.

      As for the Singleton scope issue: I am not instantiating these stateless SRP classes as Singletons because I believe it necessary for memory or performance reasons. Instead it is for the conceptual reason that the classes are stateless and so consequently it makes sense for there to be one and only one of them in existence. As for the memory issues I found, that had to do with instantiating multiple instances of StandardKernel (which admittedly is not a good idea). I had hoped that GC would clean up the old Kernel and all the Singleton classes it produced, but a memory analysis tool showed that somehow the new Kernel referenced the old. I take this to be a Ninject issue – or at least the result of using Ninject in ways not planned for by its creators.

      I am actually using Moq for all my unit tests, and only use Ninject in what I called system tests but might also be called integration tests (the term used in your post).

      Bottom line: the pattern I suggested works well for me and removes a lot of boilerplate, in its rightful context. This applies to most of the classes in my project, and has saved me time and maintenance headaches. But I don’t mean to suggest it is always the best way to use DI.

    • Json says:

      “Unless you’re performing integration tests, you shouldn’t even be using DI or creating any bindings. When testing an object, you don’t test the objects dependencies.”

      What?? You should always use DI on unit tests!! Always!! That’s the fucking point. You inject Mocked versions of those dependencies. Those dependencies are usually services or repositories. So the mocked dep. that are injected return back bogus data or success == true type crap. You are ignorant if you truly believe DI has no place in unit tests.

      ” In my experience, it’s very rare that a class should require singleton scope!”

      Then your experience is very, very limited, and limited to a single threaded, one user environment. It is not hard to create a perfectly stateless service/repository or other dependency. In fact, making those singleton forces one to create such stateless dependencies (which is good)

      Your comments really reflect the understandings of a junior level developer. I wouldn’t worry too much about your criticisms.

      • Steve says:

        “What?? You should always use DI on unit tests!! Always!! ”

        I assume he meant don’t use DI *container*s in unit tests. That is one of the major points of DI, you can do it yourself in tests and let something else take care of it for the application. It sounds like Will is using the container and his unit tests are getting their real live dependencies which makes them more like integration tests.

        “Your comments really reflect the understandings of a junior level developer.”

        Your comments really reflect someone that is a bit of a jerk.

  • Comments are closed.