gecio
gecio

Reputation: 41

Different binding in specific object subtree using Ninject

I am using Ninject to perform dependency injection in my project and I've encountered one problem. I need to specify an exception in binding in specific subtrees of my "object tree" (in meaning of inclusion not inheritance). Let's say we have few interfaces and classes (I've excluded constructors and other irrelevant stuff):

interface Wheel { ... }
class RoundWheel : Wheel { ... }
class SquareWheel : Wheel { ... }
class Mechanism { Wheel Wheel; ... }
class Bike { Wheel Wheel; ... }
class Items { Mechanism Mechanism; Bike Bike; ... }

I want to bind Wheel to SquareWheel when it's somehow included in Mechanism (Mechanism can be somewhere higher, eg. Item.Mechanism.ContainerForWheel.(any types further on).Wheel) and to RoundWheel otherwise. Now, let's look at my current solution:

IKernel kernel = new StandardKernel();
kernel.Bind<Wheel>().To<RoundWheel>();
kernel.Bind<Wheel>().To<SquareWheel>().When(x => x.ActiveBindings.Any(p => p.Service.Name == typeof(Mechanism).Name));

Items items = kernel.Get<Items>();

It works like a charm, but looks very inelegant and suboptimal. It's hard to understand clear purpose of this filtering. Do you know any other way to achieve this? Thanks in advance.

Edit 1 I forgot to mention that I don't want to put any annotations and other stuff into my classes. I want to keep everything in kernel setup/modules.

Upvotes: 0

Views: 226

Answers (1)

BatteryBackupUnit
BatteryBackupUnit

Reputation: 13233

I don't think there's really an easier way to do things. You could also explictly traverse the IContext.ParentRequest and check if there's ever a specific Mechanism. That would be a bit more explicit then using the ActiveBindings property. But will neither be faster nor result in less code.

But what you could do is applying clean code and creating your own When-Extension, so you'll end up with:

kernel.Bind<Wheel>().To<SquareWheel>()
      .When(IsDescendantOf<Mechanism1>);

private static bool IsDescendantOf<T>(IRequest request)
{
    return request.ActiveBindings.Any(p => p.Service.Name == typeof(T).Name);
}

or, using an extension method:

kernel.Bind<IWheel>().To<Wheel2>()
      .WhenIsDescendantOf(typeof(Mechanism1));

public static class NinjectWhenExtensions
{
    public static IBindingInNamedWithOrOnSyntax<T> WhenIsDescendantOf<T>(this IBindingWhenSyntax<T> syntax, Type ancestor)
    {
        return syntax.When(request => request.ActiveBindings.Any(p => p.Service.Name == ancestor.Name));
    }
}

btw., you should also be able to replace the check p.Service.Name == typeof(Mechanism).Name by p.Service == typeof(Mechanism). When the name is a match, the type shoould be, too. If you're working with interface you would have to adapt the logic, though.

Upvotes: 1

Related Questions