-
Notifications
You must be signed in to change notification settings - Fork 529
Building an IBindingResolver Component
The IBindingResolver
interface is used by Ninject to locate bindings for resolution by the Kernel. It is primarily used to map the type requested to a similar type in the binding map. Given a type it returns a collection of bindings that can potentially satisfy that type. The StandardBindingResolver
simply looks up values in the supplied dictionary:
/// <summary> /// Resolves bindings that have been registered directly for the service. /// </summary> public class StandardBindingResolver : NinjectComponent, IBindingResolver { /// <summary> /// Returns any bindings from the specified collection that match the specified service. /// </summary> /// <param name="bindings">The multimap of all registered bindings.</param> /// <param name="service">The service in question.</param> /// <returns>The series of matching bindings.</returns> public IEnumerable<IBinding> Resolve(Multimap<Type, IBinding> bindings, Type service) { return bindings[service].ToEnumerable(); } }
By creating additional binding resolvers you can affect how Ninject deals with those bindings. For instance, the OpenGenericBindingResolver
that is included in StandardKernel
adds support for a binding where the generic parameters aren’t specified: kernel.Bind(typeof(IRepository<>)).To(typeof(InMemoryRepository<>)
Let’s take a look at the source:
/// <summary> /// Returns any bindings from the specified collection that match the specified service. /// </summary> public IEnumerable<IBinding> Resolve(Multimap<Type, IBinding> bindings, Type service) { if (!service.IsGenericType || !bindings.ContainsKey(service.GetGenericTypeDefinition())) return Enumerable.Empty<IBinding>(); return bindings[service.GetGenericTypeDefinition()].ToEnumerable(); }
The first if statement simply checks to see if the class can handle the type of binding, if not it returns an empty enumerable (returning null might work, but I would not tempt the fates).
The meat of this method merely backs the generic type out to its open-generic form and looks for a matching binding and returns the result (that may be empty). Support for instantiating the open generic lives in StandardProvider.
Let’s build a binding resolver that can deal with covariance. Covariance is the ability for a generic type to stand in for a generic type with a more general type argument. For instance, with IEnumerable<out T>
, IEnumerable<Ninja>
can be substituted for IEnumerable<IWarrior>
because all Ninjas are IWarriors and T
has been marked for covariance with the out
keyword.
public class CovariantBindingResolver : NinjectComponent, IBindingResolver { /// <summary> /// Returns any bindings from the specified collection that match the specified service. /// </summary> public IEnumerable<IBinding> Resolve(Multimap<Type, IBinding> bindings, Type service) { if (service.IsGenericType) { var genericType = service.GetGenericTypeDefinition(); var genericArguments = genericType.GetGenericArguments(); if (genericArguments.Count() == 1 && genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Covariant)) { var argument = service.GetGenericArguments().Single(); return bindings.Where(kvp => kvp.Key.IsGenericType && kvp.Key.GetGenericTypeDefinition().Equals(genericType) && argument.IsAssignableFrom(kvp.Key.GetGenericArguments().Single())) .SelectMany(kvp => kvp.Value); } } return Enumerable.Empty<IBinding>(); } }
Note: I’m using the .NET 4.0 HasFlag()
method for brevity. Replace with your choice of implementation for .NET 3.5 or earlier.
The implementation of this is pretty straight forward, we make sure the type is generic and has only one parameter (this can be modified to support any number of parameters). We also check to make sure that parameter is covariant. Once that has been established we walk all of the elements in the binding map looking for keys (types) that satisfy our requirements: same generic type definition and its type parameter is a subclass of the one we’re looking for.
The last thing we need to do is hook it up. You can do this by sub-classing StandardKernel
and overriding AddComponents
or by adding the component where you build up your kernel.
kernel.Components.Add<IBindingResolver, CovariantBindingResolver>();
As IBindingResolver components are intended to deal with the binding map and don’t have any other insight into the request there aren’t a whole lot of reasons you’d build one.
Back to component extension points.
Licensed under Apache 2 License
Contents
- Home
- Why Use Ninject
- Getting Started
- Dependency Injection By Hand
- Dependency Injection With Ninject
- Injection Patterns
- Multi Injection
- Object Scopes
- Modules and the Kernel
- Providers, Factory Methods and the Activation Context
- The Activation Process
- How Injection Works
- Contextual Binding
- Conventions-Based Binding