Skip to content

Providers, Factory Methods and the Activation Context

BThomann edited this page Oct 23, 2018 · 10 revisions

Okay, remember when we said type bindings were from a service type to an implementation type? Well, we lied. You caught us.

Type binding is really a little bit more complicated than we let on. Rather than binding from one type to another, bindings are actually from a service type to a provider. Simply put, a provider is an object that can create instances of another type. It’s like a factory, but specifically designed to work with Ninject.

Providers

When you define a binding from a service type to an implementation type, like the binding from IWeapon to Sword in the previous example, you’re really declaring a binding from IWeapon to a special built-in provider called StandardProvider. This StandardProvider is like a “super-factory”, in that it can create instances of all sorts of types. It also understands how to resolve the arguments to inject into constructors.

All of this stuff happens behind the scenes, though, and you rarely need to worry about it. However, sometimes you might want to do some sort of complex custom initialization to your objects, rather than letting Ninject work its magic. If you need to (or if you’re just a control freak!), you can create your own provider and bind directly to it:

Bind<IWeapon>().ToProvider(new SwordProvider());

Providers just need to implement the IProvider interface (in Ninject.Activation):

public interface IProvider
{
    Type Type { get; }
    object Create(IContext context);
}

However rather than implement this directly it is advised that you extend the abstract type Provider<T> which includes strong typing.

public abstract class Provider<T> : IProvider
{
    protected Provider();
    public virtual Type Type { get; }
    public object Create(IContext context);
    protected abstract T CreateInstance(IContext context);
}

By using Provider<T>, custom providers just need to override the CreateInstance() method. Here’s an example of a simple provider for our Sword class:

public class SwordProvider : Provider<Sword>
{
    protected override Sword CreateInstance(IContext context)
    {
        Sword sword = new Sword();
    
        // Do some complex initialization here.
        
        return sword;
    }
}

The Activation Context

This brings up another hidden complexity in Ninject, the context. See, Ninject is pretty smart. When it’s resolving a big complex graph of objects, it keeps track of where it is, what’s being injected, who’s asking for it, etc. All of this information is stored in the context, represented by the IContext interface. If you need it, your provider can access all of this information to make decisions about how to resolve the dependencies. If you don’t need it (like in the SwordProvider above), you can just ignore it.

Each time an instance is resolved, a new child context is created and the kernel’s Get() is called again. Through this (somewhat) recursive operation, Ninject forms a stack of activation contexts, all of which are available throughout the system during the activation process.

Factory Methods

A lighter weight alternative to writing IProvider implementations is to bind a service to a delegate method.

.ToMethod(Func<IContext, T> method)

Bind<IWeapon>().ToMethod(context => new Sword());

The provided Func will be bound to the service type for deferred binding and called later when a new instance of the service (i.e. IWeapon) is required.

Ninject.Extensions.Factory

Ninject v3 brings an extension that will automatically implement factories for you: Ninject.Extensions.Factory.

Continue reading: The Activation Process