Search code examples
c#ninjectninject-conventions

FromAssembliesMatching overwrites explicitly set bindings


Is there a way to combine a call to FromAssembliesMatching to set up most interfaces automatically and some explicit bindings? I have the following code that should set all ISomething to Something automatically, and then set up a specific constructor for the IUnitOfWork interface where the concrete implementation accepts two bools as arguments:

kernel.Bind(x => x.FromAssembliesMatching("*.dll").SelectAllClasses()
        .InNamespaces("MyNamespace").BindDefaultInterface());
kernel.Bind<IUnitOfWork>().ToConstructor(x => new UnitOfWork(true, false));

However, when I try to call TryGet like this I get a null back:

kernel.TryGet<MyNamespace.IUnitOfWork>()

It works fine when I switch the order around and call Excluding for the UnitOfWork implementation explicitly on the second call:

kernel.Bind<IUnitOfWork>().ToConstructor(x => new UnitOfWork(true, false));
kernel.Bind(x => x.FromAssembliesMatching("*.dll").SelectAllClasses()
      .InNamespaces("MyNamespace").Excluding(typeof(UnitOfWork))
      .BindDefaultInterface());

Reversing the order, or leaving out the call to Excluding results in null again when I try to resolve an instance of an IUnitOfWork.

What's the proper way to combine automatic matching with explicit configuration? I am using Ninject 3.2.0.0 and Ninject.Extensions.Conventions 3.2.0.0.


Solution

  • There's two ways to handle your situation, one you've already found:

    • Excluding<> a type from the convention.
      • Advantage: This is agnostic to the sequence of your "manual" and your convention bindings.
      • Disadvantage: you need to refer to special cases in two places, in the convention and in the "manual" binding
    • using Rebind<IUnitOfWork> instead of Bind.
      • Advantage: you don't need to refer to the special case in two places.
      • Disadvantage: This only works if you perform the convention binding before doing the Rebind.

    Of course you could also add an attribute to all types which should be excluded from conventions and adapt your conventions accordingly. But i doubt that that would be any better.

    By the way, one could imagine something like extending the convention to check whether a binding for that type already exists, and only creating a new binding if it doesn't. Besides the obvious complications with multi- and contextual-bindings, ninject has no easy access to check whether a binding for an "implementation" type already exists. All there is is IKernel.GetBindings(Type serviceType). And due to some internals this can't be changed easily.