-
Notifications
You must be signed in to change notification settings - Fork 529
Component Extension Points
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. Learn more about building an IBindingResolver Component.
When a binding can’t be found in the bindings map Ninject moves on to using IMissingBindingResolver
components to try and locate a binding. The main difference between IBindingResolver
and IMissingBindingResolver
is that the latter creates bindings out of thin air to handle the request. Learn more about building an IMissingBindingResolver Component.
The pipeline consists of a list of IActivationStrategy
components that are run whenever an object is created. The built in Pipeline
class is a simple coordinator that makes use of an IActivationCache
component to keep from activating an instance multiple times. It’s unlikely a new IPipeline
implementation will be needed, as most tasks can be accomplished by implementing one of the IActivation*
interfaces, but you can use it to pull strategies from a different location (instead of kernel.Components
) or to selectively run strategies based on the context.
IPipeline
is a simple interface: a property that lists the strategies in play and methods for activation and deactivation.
The activation cache simply holds sets of references that have been activated and deactivated. One thing to note is that the IActivationCache
is used in two places: IPipeline
takes it to check whether it should activate a given instance, and ActivationCacheStrategy
(an implementation of IActivationStrategy
) takes it to actually add the instance to the cache. Be aware of this separation and its implications (for instance, your IActivationCache
implementation should be thread safe).
Objects created by Ninject are activated after creation and deactivated when they are manually released. These strategies can do pretty much anything they want, for example property and method injection are actually implemented as strategies. When building your own IActivationStrategy
you can derive from ActivationStrategy
to simplify your implementation.
A good example of an activation strategy is the built-in StartableStrategy
. It checks to see if the instance is of type IStartable
and if so calls Start or Stop on it.
public class StartableStrategy : ActivationStrategy { public override void Activate(IContext context, InstanceReference reference) { reference.IfInstanceIs<IStartable>(x => x.Start()); } public override void Deactivate(IContext context, InstanceReference reference) { reference.IfInstanceIs<IStartable>(x => x.Stop()); } }
When Ninject needs to know which members of a class should be injected it looks to the ISelector
component. In addition to holding the constructor scorer and the injection heurestics (see below), it also returns a list of injectable constructors, properties, and methods. Extension is pretty straightforward, get a list of the members, filter them, and then return them. If there are no constructors then the convention is to return null; for methods and properties return an empty IEnumerable
.
For our example, let’s say that some of our classes take a special debug type that we only use during testing and debugging. For production we want to use the version of the constructor that does not take that type.
public class MyClass { public MyClass() { } public MyClass(DebugType dT) { } }
Since Ninject likes to pick the constructor with the most arguments it will always choose the second constructor. Here’s an example of how we would skip the debug constructor in production mode:
public class NoDebugSelector : Selector { public NoDebugSelector(IConstructorScorer constructorScorer, IEnumerable<IInjectionHeuristic> injectionHeuristics) : base(constructorScorer, injectionHeuristics) {} public override IEnumerable<ConstructorInfo> SelectConstructorsForInjection(Type type) { if(type == null) throw new ArgumentNullException("type"); return type.GetConstructors(this.Flags) .Where(c => !c.GetParameters() .Select(p => p.ParameterType) .Contains(typeof (DebugType))) .ToArray(); } }
Note that a more DI convention would be to wrap DebugType in in interface or proxy and bind Ninject to return the real or stub verion of the interface depending on the configuration. Occaisonally, though, the above pattern is unavoidable.
Sometimes it may be simpler to modify the heurestics or the constructor scorer.
When the ISelector
is looking to decide whether it should select a property or method for injection it looks to the IInjectionHeuristic
interface. This interface has a single method which takes a reflection MemberInfo
and returns a boolean indicating whether it should inject. The standard heurestic chooses any member that has the [Inject]
attribute. Here is an example that always injects our logging type s othat we don’t have to specifically add the [Inject]
attribute every time:
public class MyHeuristic : NinjectComponent, IInjectionHeuristic { public bool ShouldInject(MemberInfo member) { var propertyInfo = member as PropertyInfo; return propertyInfo != null && propertyInfo.PropertyType == typeof (ILog); } }
Ninject runs each constructor through the IConstructorScorer
interface to determine which constructor it should use to instantiate the class. After scoring each constructor, Ninject chooses the one with the highest score. By adjusting the score you can affect which constructor can be chosen. For instance, to continue our scenario from the ISelector
example we could instead choose to implement it like this:
public class MyScorer : StandardConstructorScorer { public override int Score(IContext context, ConstructorInjectionDirective directive) { if(directive.Targets.Select(t => t.Type).Contains(typeof(DebugType))) { return Int32.MinValue; } return base.Score(context, directive); } }
Ninject only allows one
IConstructorScorerto be registered at a time.
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