HowTo – NInject – Part 2 – Advanced Features

Source Code

All source code can be found on GitHub here.

My cheat sheet for NInject can be found here.

This is part of my HowTo in .NET seriies. An overview can be seen here,

This post is  a two part post on NInject:

  1. Basics
  2. Advanced Features

Bind To Constant

We can bind Interfaces and Classes to an actual instance or constant, a pseudo Singleton pattern.

var kernel = new StandardKernel();
var firstKnownC = new KnownC();

kernel.Bind<IClass>().ToConstant(firstKnownC);

var secondKnownC = kernel.Get<IClass>();

Assert.That (secondKnownC, Is.InstanceOf<IClass>());
Assert.That (secondKnownC, Is.InstanceOf<KnownC>());
Assert.That (secondKnownC, Is.SameAs(firstKnownC));

Bind to Constructor

We can bind to a delegate which returns a new instance

var kernel = new StandardKernel();
kernel.Bind<IClass>().ToConstructor( x => new KnownC());

var aClass = kernel.Get<IClass>();

Assert.That (aClass, Is.InstanceOf<IClass>());
Assert.That (aClass, Is.InstanceOf<KnownC>());

If we need to ask the kernel to resolve an instance it is passed into the method as the only parameter.

Bind<IMyService>().ToConstructor( ctorArg => new ClassC(ctorArg.Inject<IClassA>(), ctorArg.Inject<IClassB>()));

Bind To Method

Binds to a method; similar to a factory method.

var kernel = new StandardKernel();
kernel.Bind<IClass>().ToMethod(x => new KnownC());

var aClass = kernel.Get<IClass>();

If we need to ask the kernel for a resolution of a concrete instance:

Bind<IClass>().ToMethod( x => new ClassC(x.Kernel.Get<IClassA>(), x.Kernel.Get<IClassB>()));

Bind To Provider

Providers provide way to provide complex initiation.

var kernel = new StandardKernel();

kernel.Bind<IClass>().ToProvider( new KnownCProvider());

var aClass = kernel.Get<IClass>();

Assert.That(aClass, Is.InstanceOf<IClass>());
Assert.That(aClass, Is.InstanceOf<KnownC>());

public class KnownCProvider : Provider<KnownC>
{
	protected override KnownC CreateInstance(IContext context)
	{
		var aClass = new KnownC(); // We could do some complex initialisation here.
			return aClass;
	}
}

Contextual Binding

We can specify rules as to what class instance implementing an interface is injected into a class. This way we can provide logic for what is injected into a class based upon any inherited class (directly or indirectly inherited) or directly inherited.

WhenInjectedInto is determined as the ancestral class as any within the hierarchy.
WhenInjectedExactlyInto is determined as the immediate ancestral class only.

In the example below:

ChildA and ChildB both require an instance of IInject which is implemented by IInjectA and IInjectB, we can provide logic to determine which implementing instance of IInject is injected into the constructor of ChildA and ChildB.

InjectA is injected into ChildA because its parent ParentA inherits GrandParent and is bound with WhenInjectedInto.
InjectB is injected into ChildB because its parent is ParentB and is bound with WhenInjectedExactlyInto.

public class ParentA : GrandParent, IParent
{
	public ParentA ()
	{
	}
}

public class ChildA : ParentA, IChild
{
	public IInject Inject { get; set; }

	public ChildA ( IInject inject  )
	{
		Inject = inject;
	}
}

public class ChildB : ParentB, IChild
{
	public IInject Inject { get; set; }

	public ChildB ( IInject inject)
	{
		Inject = inject;
	}
}

var kernel = new StandardKernel ();

kernel.Bind<ChildA> ().ToSelf ();
kernel.Bind<ChildB> ().ToSelf ();
kernel.Bind<IInject> ().To<InjectA> ().WhenInjectedInto (typeof(GrandParent));
kernel.Bind<IInject> ().To<InjectB> ().WhenInjectedExactlyInto (typeof(ChildB));

var childA = kernel.Get<ChildA> ();
var childB = kernel.Get<ChildB> ();

Allow us to write our own attribute, tag classes with these attributes and provide conditional for injection around these attributes.

We can decorate parameters, methods/properties and classes with these attributes

The Target: Is considered a parameter Method or Constructor decorated.
The Member: Is a property decorated
The Class: Is a class decorated.

First we need to define our own parameters

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
public class WhenTargetHas : Attribute { }

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
public class WhenMemberHas : Attribute{ }

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class WhenClassHas : Attribute { }

Then we decorate, Properties, Parameters and Classes with these attributes.

public class InjectWhenMemberAndTargetHas
{
	[Inject, WhenMemberHas]
	public IClass WhenMemberHas { get; set; }

	public IClass WhenTargetHas { get; set; }

	public InjectWhenMemberAndTargetHas([WhenTargetHas] IClass aClass)
	{
		WhenTargetHas = aClass;
	}
}

[WhenClassHas]
public class InjectWhenClassHas
{
	[Inject]
	public IClass WhenClassHas { get; set; }
}

Then we bind IClass to concrete types

var kernel = new StandardKernel ();

kernel.Bind<IClass>().To<KnownC>().WhenClassHas<WhenClassHas>();
kernel.Bind<IClass>().To<KnownD>().WhenTargetHas<WhenTargetHas>();
kernel.Bind<IClass>().To<KnownE>().WhenMemberHas<WhenMemberHas>();

Here we can conditionally bind IClass to KnownC,KnownD and KnownE such that we can control which class instance is injected into each entity.

InjectWhenClassHas has KnownC injected into the public property WhenClassHas as the class has the attribute [WhenClassHas] and the binding of IClass to KnownC WhenClassHas.

InjectWhenMemberAndTargetHas has KnownD injected into the constructor argument of IClass which is stamped with the attribute [WhenTargetHas] as we bind with WhenTargetHas.

InjectWhenMemberAndTargetHas has KnownE injected into the public property WhenMemberHas as the property is stamped with WhenMemberHas attribute and is bound to inject to KnownE when a property (or method) has the attribute.

Named binding provides a way to bind multiple instances to a entity by an arbitrary name and then to resolve the correct instance by providing the required name.

var kernel = new StandardKernel ();

kernel.Bind<IClass>().To<KnownC>().Named("NameknownC");
kernel.Bind<IClass>().To<KnownD>().Named("NameknownD");

KnownC classC = aKernel.Get<IClass>("NameknownC");
KnownD classD = aKernel.Get<IClass>("NameknownD");

Parameters can be stamped with the required binding name

public ClassC ( [Named("First") IClass aClass]
{}

With Meta Data or Attribute Based Helpers

Custom Attributes can contain conditional logic which allows the attribute to determine if a resolution should be made. Logic is made against tags and the values which are provided as part of the binding.

First we define an Attribute with logic

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
                AllowMultiple = true, Inherited = true)]
public class IsSomething : ConstraintAttribute
{
	public override bool Matches (IBindingMetadata metadata)
	{
		return metadata.Has ("IsSomething") && metadata.Get<bool> ("IsSomething");
	}
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
	                AllowMultiple = true, Inherited = true)]
	public class IsNotSomething : ConstraintAttribute
	{
		public override bool Matches (IBindingMetadata metadata)
		{
			return metadata.Has ("IsSomething") && !metadata.Get<bool> ("IsSomething");
		}
	}

We can then tag elements which require conditional injection

public class InjectWithIsSomething
{
	public IClass InjectedClass{ get; set; }

	public InjectWithIsSomething([IsSomething] IClass aClass)
	{
		InjectedClass = aClass;
	}
}

public class InjectWithIsNotSomething
	{
		public IClass InjectedClass{ get; set; }

		public InjectWithIsNotSomething([IsNotSomething] IClass aClass)
		{
			InjectedClass = aClass;
		}
	}

Both attributes provide logic for determining when they qualify based upon the expected values of IsSomething attribute. We assign the IsSomething value at bind time.

Bind<IClass> ().To<KnownC> ().WithMetadata ("IsSomething", true);
Bind<IClass> ().To<KnownD> ().WithMetadata ("IsSomething", false);

The resolve asks the attribute if the condition logic is met

var isSome =  kernel.Get<InjectWithIsSomething>();
var isNotSome =  kernel.Get<InjectWithIsNotSomething>();

In the example above we resolve IClass to either KnownC or KnownD based upon whether the parameter is decorated with [IsSomething] or [IsNotSomething] and the value to the IsSomething tag which is provided as part of binding.

Conditional Binding

We can also provide conditional logic by providing a delegate. In this case are asking the class its name and namespace.

Bind<IClass>().To<ClassA>().When(request => request.Target.Member.Name.StartsWith("ClassName"));
Bind<IClass>().To<ClassB>().When(request => request.Target.Type.Namespace.StartsWith("NameSpace.ClassName"));
Advertisements

One thought on “HowTo – NInject – Part 2 – Advanced Features

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s