Main image
10th May
2013
written by Chris

Inversion of Control (IOC) frameworks have become quite the rage as the software craftmanship movement has gathered steam. IOC makes it much easier to break complex multi-part programs into distict, and more importantly testable, components so that they can then be glued back together at run time. There are lots of nifty frameworks that can make this happen but at the moment I’m playing with Ninject.

Ninject is a very minimalistic IOC framework which focuses on what it calls a “fluent interface” that leverages the compiler and IDE rather than a huge XML file to map dependencies. Overall it’s very fast, very light weight, and very powerful. I’ve picked it up quickly and found it to meet almost all of my IOC needs.

Save one. And this is apparently a big problem for a lot of people. Ninject doesn’t like overloaded constructors.

Let’s define some quick Ninject bindings so we have a place to start:

class NinjectTestModule : NinjectModule
{
     public override void Load()
     {
          //Dependencies for the "StandAloneUser" Object
          Bind<IUserDataMappingDAO>().To<UserDataMappingMSSQLDAO>();
          Bind<IRoles>().To<RolesMockImpl>();
          Bind<IUserGroupAssociationDAO>().To<UserGroupAssociationDAO>();
          Bind<IGroupDAO>().To<GroupMSSQLDAO>();
          Bind<IEmailDAO>().To<EmailMSSQLDAO>();

          //Bindings for StandAloneUser
          Bind<StandAloneUser>().ToSelf(); //Unnecessary, happens automagically

          //Things we can use to build a "StandAloneUser" Object
          Bind<MembershipUser>().ToSelf();
          Bind<PortableStandAloneUser>().ToSelf();
     }
}

Ninject’s syntax is fairly easy to read but if you’re unfamiliar with it, it does exactly what it looks like it does. The first “Bind” command above tells Ninject that the UserDataMappingMSSQLDAO type (which implements IUserDataMappingDAO) should be used whenever something needs something of the type IUserDataMappingDAO. That’s handy, because when we instantiate a StandAloneUser object we need lots of components. Here are a couple constructor signatures:

public StandAloneUser(PortableStandAloneUser portableStandAloneUser, IUserDataMappingDAO userDataMappingDAO, IRoles roles, IUserGroupAssociationDAO userGroupAssociationDAO, IGroupDAO groupDAO, APortalLogger logger, IEmailDAO emailDAO)

public StandAloneUser(MembershipUser membershipUser, IUserDataMappingDAO userDataMappingDAO, IRoles roles, IUserGroupAssociationDAO userGroupAssociationDAO, IGroupDAO groupDAO, APortalLogger logger, IEmailDAO emailDAO)

public StandAloneUser(IUserDataMappingDAO userDataMappingDAO, IRoles roles, IUserGroupAssociationDAO userGroupAssociationDAO, IGroupDAO groupDAO, APortalLogger logger, IEmailDAO emailDAO)

Now here’s our first problem. Whenever I ask Ninject to cough up a StandAloneUser it will never use that third constructor. Why? Because we have bindings for MembershipUser and PortableStandAloneUser. Since Ninject always selects the constructor for which it has the most bound arguments, the third constructor (having fewer arguments as well as fewer bound arguments) will never be considered. What’s more, this means that even if we don’t supply a PortableStandAloneUser or a MembershipUser to Ninject when asking for a StandAloneUser, Ninject will happily create one for us so it can use the more fleshed out constructor.

Actually, the StandAloneUser object has more problems than that. Because the first and second constructors have the same number of bound arguments, Ninject can’t decide which constructor to use and will throw an error. This is the first sign that something is, as the Bard wrote, “foul in the state of Denmark.” Ninject’s objection to a class with two constructors having the same number of bound arguments indicates that the Ninject framework does not understand overloading. Without Ninject we would just call those constructors normally and the compiler would work out which one we wanted by looking at the arguments passed in but Ninject can’t do that; the uniqueness of a constructor’s signature is, insofar as Ninject is concerned, defined only by the number of bound parameters it contains. That means that these two calls to Ninject’s “Get” function, despite looking for all the world like they should call different constructors, won’t:

StandardKernel _kernel = new StandardKernel(new NinjectTestModule());
_kernel.Get<StandAloneUser>(new ConstructorArgument("user", new MembershipUser()));
_kernel.Get<StandAloneUser>(new ConstructorArgument("portableStandAloneUser", new PortableStandAloneUser()));

So deep does this assumption run that Ninject will attempt to cast MembershipUsers as PortableStandAloneUsers (or vice versa) in order to fulfill the constructor requirements rather than looking for another constructor which matches the provided signature.

But there is a solution. Down deep in the undocumented bowels of the Ninject framework is a function called “ToConstructor” which functions a lot like “To” but takes a callback to a constructor of your choice. This, coupled with some contextual binding, can get us out of this trap. Here is our new NinjectTestModule with our improved binding for StandAloneUser:

class NinjectTestModule : NinjectModule
{
     public override void Load()
     {
          //Dependencies for the "StandAloneUser" Object
          Bind<IUserDataMappingDAO>().To<UserDataMappingMSSQLDAO>();
          Bind<IRoles>().To<RolesMockImpl>();
          Bind<IUserGroupAssociationDAO>().To<UserGroupAssociationDAO>();
          Bind<IGroupDAO>().To<GroupMSSQLDAO>();
          Bind<IEmailDAO>().To<EmailMSSQLDAO>();

          //Bindings for StandAloneUser
          Bind<StandAloneUser>()
                .ToConstructor(
                    x =>
                    new StandAloneUser(x.Inject<MembershipUser>(), x.Inject<IUserDataMappingDAO>(), x.Inject<IRoles>(),
                                       x.Inject<IUserGroupAssociationDAO>(), x.Inject<IGroupDAO>(),
                                       x.Inject<APortalLogger>(), x.Inject<IEmailDAO>()))
                .Named("FromMembershipUser");
            Bind<StandAloneUser>()
                .ToConstructor(
                    x =>
                    new StandAloneUser(x.Inject<PortableStandAloneUser>(), x.Inject<IUserDataMappingDAO>(), x.Inject<IRoles>(),
                                       x.Inject<IUserGroupAssociationDAO>(), x.Inject<IGroupDAO>(),
                                       x.Inject<APortalLogger>(), x.Inject<IEmailDAO>()))
                .Named("FromPortableStandAloneUser");

          //Things we can use to build a "StandAloneUser" Object
          Bind<MembershipUser>().ToSelf();
          Bind<PortableStandAloneUser>().ToSelf();
     }
}

A good deal less light-weight, but it works! We now have two “named” bindings for StandAloneUser which point to two different but overloaded constructors this way we can get Ninject to produce a StandAloneUser from either a PortableStandAloneUser or a MembershipUser. The Kernel calls are a little difficult to read but so long as the naming convention isn’t too objectionable, easy enough to figure out.

StandAloneTestUser1 = _kernel.Get<StandAloneUser>("FromMembershipUser", new ConstructorArgument("user", aMembershipUser));
StandAloneTestUser2 = _kernel.Get<StandAloneUser>("FromPortableStandAloneUser", new ConstructorArgument("user", aPortStandAloneUser));

Now if there were only a way to do it without the names, you say.

But of course there is.  There is an axiomatic truth to any good framework and that is that you need a Really Good Reason to try to circumvent the whole thing.  All of that nonsense about named constructors and ConstructorArguments above?  That’s an elaborate end-run around what Ninject really wants you to do.

So how does Ninject want us to handle this need to build StandAloneUsers from MembershipUsers, PortableStandAloneUsers, and (what the heck) Guids to boot?  Ninject wants us to use a factory.

So instead of all of this rigamarole we create a new class called StandAloneUserFactory.  StandAloneUserFactory can be built by Ninject; it only has one constructor (which is chock-full of dependencies); and it passes all of those dependencies right into each StandAloneUser it creates.  So now, instead of constructing a StandAloneUser as we do above, we do it like this

StandAloneUserFactory factory = _kernel.Get<StandAloneUserFactory>();
StandAloneUser fromMembership = factory.Get(aMembershipUser);
StandAloneUser fromPortable = factory.Get(aPortableUser);
StandAloneUser fromGuid = factory.Get(aGuid);

Overloading and IOC: the best of both worlds.

Leave a Reply