4
votes

Is it possible to set up a ninject binding to respect a generic constraint?

For example:

interface IFoo { }
interface IBar { }
interface IRepository<T> { }

class FooRepository<T> : IRepository<T> where T : IFoo { }
class BarRepository<T> : IRepository<T> where T : IBar { }

class Foo : IFoo { }
class Bar : IBar { }

class Program
{
    static void Main(string[] args)
    {
        IKernel kernel = new StandardKernel();

        // Use this binding only where T : IFoo
        kernel.Bind(typeof(IRepository<>)).To(typeof(FooRepository<>));

        // Use this one only where T : IBar
        kernel.Bind(typeof(IRepository<>)).To(typeof(BarRepository<>));

        var fooRepository = kernel.Get<IRepository<Foo>>();
        var barRepository = kernel.Get<IRepository<Bar>>();
    }
}

Calling this code as-is will produce a multiple activation paths exception:-

Error activating IRepository{Foo}: More than one matching bindings are available.

How can I set up the bindings to be conditional on the value of T? Ideally I'd like them to pick up the constraints from the target type, as I've already defined them there, but if I have to define them again in the binding that's also acceptable.

1
@Stephen: I don't think the call stack is really necessary. It makes the question misleading as the problem isn't that I have defined two bindings for IRepository<>, it's that I want to make them both conditional on the value of T, respecting the generic type constraint on the target type as per the comments in my code.Iain Galloway
It's your question, so feel free to remove the stack trace ;-). However, since I was under the impression that your registration would work, I thought it was good to show that it didn't. Perhaps I agree with you that just the error message would be enough info. It's unfortunate that Ninject doesn't seem to support this scenario (out of the box), while other frameworks, such as Autofac and Simple Injector do support this.Steven
Cool, I've edited and left the error message in. As you say, it's probably important to show more clearly that it doesn't work as-is. You say this works out-of-the-box in Autofac? Maybe that's my solution then...Iain Galloway
Yes, I believe it does work OOTB with Autofac, but I'm don't know exactly how to do this (I do know how to do this with Simple Injector though).Steven

1 Answers

1
votes

Maybe there is a cleaner solution but it definitely works with the When contextual binding method and some reflection:

// Use this binding only where T : IFoo
kernel.Bind(typeof(IRepository<>)).To(typeof(FooRepository<>))
   .When(r => typeof(IFoo).IsAssignableFrom(r.Service.GetGenericArguments()[0]));

// Use this one only where T : IBar
kernel.Bind(typeof(IRepository<>)).To(typeof(BarRepository<>))
  .When(r =>  typeof(IBar).IsAssignableFrom(r.Service.GetGenericArguments()[0]));