Skip to content
remogloor edited this page Feb 20, 2012 · 9 revisions

The internals of Ninject are broken up into components that are wired together using a lightweight Inversion of Control container (yes, Ninject is actually two Dependency Injection frameworks). This allows custom behavior to be inserted into the resolution pipeline. The components that Ninject use are all accessed from the Components property off of IKernel. These components are generally loaded from a subclass of KernelBase such as StandardKernel but can also be added externally.

This is StandardKernel.AddComponents() as it exists in version 2.2:


Components.Add<IPlanner, Planner>();
Components.Add<IPlanningStrategy, ConstructorReflectionStrategy>();
Components.Add<IPlanningStrategy, PropertyReflectionStrategy>();
Components.Add<IPlanningStrategy, MethodReflectionStrategy>();

Components.Add<ISelector, Selector>();
Components.Add<IConstructorScorer, StandardConstructorScorer>();
Components.Add<IInjectionHeuristic, StandardInjectionHeuristic>();

Components.Add<IPipeline, Pipeline>();
Components.Add<IActivationStrategy, ActivationCacheStrategy>();
Components.Add<IActivationStrategy, PropertyInjectionStrategy>();
Components.Add<IActivationStrategy, MethodInjectionStrategy>();
Components.Add<IActivationStrategy, InitializableStrategy>();
Components.Add<IActivationStrategy, StartableStrategy>();
Components.Add<IActivationStrategy, BindingActionStrategy>();
Components.Add<IActivationStrategy, DisposableStrategy>();

Components.Add<IBindingResolver, StandardBindingResolver>();
Components.Add<IBindingResolver, OpenGenericBindingResolver>();

Components.Add<IMissingBindingResolver, DefaultValueBindingResolver>();
Components.Add<IMissingBindingResolver, SelfBindingResolver>();

#if !NO_LCG
if (!Settings.UseReflectionBasedInjection)
{
Components.Add<IInjectorFactory, DynamicMethodInjectorFactory>();
}
else
#endif
{
Components.Add<IInjectorFactory, ReflectionInjectorFactory>();
}

Components.Add<ICache, Cache>();
Components.Add<IActivationCache, ActivationCache>();
Components.Add<ICachePruner, GarbageCollectionCachePruner>();

#if !NO_ASSEMBLY_SCANNING
Components.Add<IModuleLoader, ModuleLoader>();
Components.Add<IModuleLoaderPlugin, CompiledModuleLoaderPlugin>();
#endif

Component Extension Points

IBindingResolver

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.

IMissingBindingResolver

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.

IPipeline

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.

IActivationCache

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).

IActivationStrategy

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());
  }
}

ISelector

IInjectionHeuristic

IConstructorScorer

IPlanner

IPlanningStrategy

IInjectorFactory

ICache

ICachePruner

IModuleLoader

IModuleLoaderPlugin

KernelBase Extension Points

You can also affect the behavior of Ninject by deriving from KernelBase itself and overriding important functionality. Note that this is more brittle than the interfaces as often times core functionality is rewritten as components and this can make your changes incompatibile with future versions.

Inject

/// <summary>
/// Injects the specified existing instance, without managing its lifecycle.
/// </summary>
/// <param name="instance">The instance to inject.</param>
/// <param name="parameters">The parameters to pass to the request.</param>
void Inject(object instance, params IParameter[] parameters)

Release

/// <summary>
/// Deactivates and releases the specified instance if it is currently managed by Ninject.
/// </summary>
/// <param name="instance">The instance to release.</param>
/// <returns><see langword="True"/> if the instance was found and released; otherwise <see langword="false"/>.</returns>
bool Release(object instance)

CanResolve

/// <summary>
/// Determines whether the specified request can be resolved.
/// </summary>
/// <param name="request">The request.</param>
/// <returns><c>True</c> if the request can be resolved; otherwise, <c>false</c>.</returns>
bool CanResolve(IRequest request)

Resolve

/// <summary>
/// Resolves instances for the specified request. The instances are not actually resolved
/// until a consumer iterates over the enumerator.
/// </summary>
/// <param name="request">The request to resolve.</param>
/// <returns>An enumerator of instances that match the request.</returns>
IEnumerable<object> Resolve(IRequest request)

CreateRequest

/// <summary>
/// Creates a request for the specified service.
/// </summary>
/// <param name="service">The service that is being requested.</param>
/// <param name="constraint">The constraint to apply to the bindings to determine if they match the request.</param>
/// <param name="parameters">The parameters to pass to the resolution.</param>
/// <param name="isOptional"><c>True</c> if the request is optional; otherwise, <c>false</c>.</param>
/// <param name="isUnique"><c>True</c> if the request should return a unique result; otherwise, <c>false</c>.</param>
/// <returns>The created request.</returns>
IRequest CreateRequest(Type service, Func<IBindingMetadata, bool> constraint, IEnumerable<IParameter> parameters, bool isOptional, bool isUnique)

BeginBlock

/// <summary>
/// Begins a new activation block, which can be used to deterministically dispose resolved instances.
/// </summary>
/// <returns>The new activation block.</returns>
IActivationBlock BeginBlock()

GetBindings

/// <summary>
/// Gets the bindings registered for the specified service.
/// </summary>
/// <param name="service">The service in question.</param>
/// <returns>A series of bindings that are registered for the service.</returns>
IEnumerable<IBinding> GetBindings(Type service)

It is preferable to create a component that implements IBindingResolver.

GetBindingPrecedenceComparer

/// <summary>
/// Returns an IComparer that is used to determine resolution precedence.
/// </summary>
/// <returns>An IComparer that is used to determine resolution precedence.</returns>
IComparer<IBinding> GetBindingPrecedenceComparer()

This method returns an IComparer that decides the precendence of bindings. The default is to prefer explicit bindings over implicit and contextual bindings over unconditional bindings.

SatifiesRequest

/// <summary>
/// Returns a predicate that can determine if a given IBinding matches the request.
/// </summary>
/// <param name="request">The request/</param>
/// <returns>A predicate that can determine if a given IBinding matches the request.</returns>
Func<IBinding, bool> SatifiesRequest(IRequest request)

HandleMissingBinding

/// <summary>
/// Attempts to handle a missing binding for a request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns><c>True</c> if the missing binding can be handled; otherwise <c>false</c>.</returns>
bool HandleMissingBinding(IRequest request)

It is preferable to create a component that implements IMissingBindingResolver.

CreateContext

/// <summary>
/// Creates a context for the specified request and binding.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="binding">The binding.</param>
/// <returns>The created context.</returns>
IContext CreateContext(IRequest request, IBinding binding)