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