Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

NInject Contextual Binding, or “How to defy Mick Jagger and always get what you want”

Going ahead with this post without a full investigation because, well, isn’t that pretty much the defining characteristic of a blog?

I’m working on an application that reads data from a database and dumps it to an XML file. So I’m trying to figure out how to use NInject to inject dependencies into an SSIS package that uses managed code. I almost have it working but I have one small issue with the…

Haha, I’m kidding of course. But I would love to have seen the collective spit takes all of you did when you read that.

In fact, it’s a console app. It takes a single parameter which determines what kind of entity we want to export. Hypothetically, let’s call them Critters, Recipes, HuntingGrounds.

The export process is identical for each of these: retrieve data from a repository and iterate over each item, dumping it to a file. The only difference is which repository is used to get the data. Which is where contextual binding comes in.

This is my first foray into NInject which is my way of seguing into “If you know a better way, I’m all ears.” I’ll skip the NInject overview because it’s described on their wiki. Instead, here are the important bits of the module:

public class AIDependencyModule : StandardModule
{
    private readonly string _exportType;

    public AIDependencyModule( string exportType )
    {
        _exportType = exportType;
    }

    public override void Load()
    {
        // ... Bind other dependencies
        Bind<IExportRepository>().To<CritterRepository>()
            .OnlyIf( l => _exportType == "critter" );
        Bind<IExportRepository>().To<RecipeRepository>()
            .OnlyIf( l => _exportType == "recipe" );
        Bind<IExportRepository>().To<HuntingGroundRepository>()
            .OnlyIf( l => _exportType == "huntingground" );
    }
}

This is all shiny and happy and works swimmingly. When my data reader class thingy needs a repository to retrieve the data, the correct one is used based on the exportType, which is supplied via command-line arguments. Pretty much a text-book case for contextual binding.

But it’s not what I really wanted to do when I first looked into this. Initially, I was looking for syntax like this:

To clarify, this is NOT valid NInject code.

public class AIDependencyModule : StandardModule
{
    private readonly string _exportType;

    public AIDependencyModule( string exportType )
    {
        _exportType = exportType;
    }

    public override void Load()
    {
        // ... Bind other dependencies
        Bind<CritterRepository>().ToSelf().WithName("critter");
        Bind<RecipeRepository>().ToSelf().WithName("recipe");
        Bind<HuntingGroundRepository>().ToSelf().WithName("huntingground");

        Bind<IExportRepository>().ToItemWithName(_exportType);
    }
}

That is, I want to register all of the repositories in the container, then bind IExportRepository to one based on the name. This seemed cleaner to me. If I want to add support for some other type, I can just added its repository to the container and be done with it. Instead of adding conditional logic to the IExportRepository binding.

That said, this is less efficient than the way I’ve actually done it above. In that case, the only repository in the container is the one I need. With my fanciful, non-existent way, I’d add a bunch of repositories, then end up using only one (unless I actually do need the other repositories elsewhere, which is not the case here).

To provide full disclosure, I had initially started typing this post with the aim being “why can’t I do it the way I want to.” It wasn’t until I started getting the ideas down that I realized my way would be sub-optimal. Just the same, I’m wondering:

  • a) if providing bindings based on name might be useful in other contexts, or
  • b) if it’s already available, how does one go about it?

Kyle the Contextual

This entry was posted in NInject. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Kyle Baley

    Always knew I’d be called out for vernacular someday. Though I have to admit I didn’t think it would be for swimmingly.

  • Wade

    “swimmingly”

    Who talks like that?

  • Matthew

    @Brian

    Thanks for the feedback – I’ve read your post in reply to my comments as well as your and Ayende’s previous posts (which i hadn’t come across before) and i can definitely empathize with what you’re saying.

    I’ve done a fair amount of ETL using SSIS but most of it has been from/to SQL Server and importing from flat/Excel files so i wasn’t aware of the seemingly extremely poor support for XML.

    My main complaints about it are the virtually useless error messages and the absolute grief associated with trying to deploy packages across environments, especially when they form only a small part of a much larger automated workflow process.

    Having said that, i always seem to consider it when deciding how to go about something because in my view, despite the grief it can cause, when it works it works quite well and can be a considerable time savior, but i agree with everything you’ve said about it.

  • http://persistall.com Brian Donahue

    @Matthew
    Kyle asked me if I could elaborate on why we chose not to use SSIS, and I couldn’t resist another chance to vent. Results can be found here:
    http://is.gd/m7xu

  • Matthew

    Kyle: You mentioned the potential use of SSIS to do this kind of export but you mentioned it seemingly more as a joke rather than anything else.

    I have on occasions had to decide between using SSIS or writing something of my own to do this kind of data exporting so i’m wondering why it is that you decided to go with custom code rather than SSIS?

    You would of course probably not be able to use DI with SSIS and it would probably end up being a more tighly coupled solution but are those the reasons you opted against it?

  • http://www.codinginstinct.com Torkel

    Great Post, and very clean solution.

    I did something very similar to this 2 years ago with Castle Windsor (similar in function not implementation):
    http://www.codinginstinct.com/2008/04/extending-castle-windsor-for-dynamic.html

  • Kyle Baley

    Nate: That’s good to know.I may just get the latest and switch over to my initial version just because I can.

    m4bwav: I am not only always getting what I want, I am also GETTING SOME SATISFACTION!

  • m4bwav

    I liked your article, it was well written, but I think I have a small criticism of one of your concepts. Listen, you never f’king defy Jagger, even if you can, you just don’t do it. :)

  • http://kohari.org/ Nate Kohari

    Nice post Kyle! Actually, if you take a look at the Ninject2 beta, the WithName() syntax is now valid. :) The contextual binding system has been dramatically improved in Ninject2… Ninject1 only let you hook conditions onto your bindings, and now you can do what I’m calling “constrained resolution”, which is something like:

    Bind().To().WithName(“a”);
    Bind().To().WithName(“b”);

    kernel.Get(“a”);

    This might seem simplistic, and lots of other containers have the same support, but you can use it to do fancier things like this:

    Bind().To().WithMetadata(“color”, “red”);
    Bind().To().WithMetadata(“color”, “red”);
    Bind().To().WithMetadata(“color”, “blue”);

    // This will return FooA and FooB:
    kernel.GetAll(x => x.Get(“color”) == “red’);

    If you’re interested, you can check out Ninject2 in its current home in Subversion, but it’ll be moving to the trunk pretty soon:

    http://ninject.googlecode.com/svn/experiments/ninject2