-
Notifications
You must be signed in to change notification settings - Fork 529
Providers, Factory Methods and the Activation Context
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.
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;
}
}
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.
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 v3 brings an extension that will automatically implement factories for you: Ninject.Extensions.Factory.
Continue reading: The Activation Process
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