Ninject 3.0 release candidate has gone live. This release introduces some new feature and some changes in its behavior. This blog post will give you detailed information what has changed. It covers mainly Ninject core. Other blog posts that describe the changes and the new extensions will follow soon.
New fluent syntax to bind multiple interfaces to the same service
One of the big lacks of previous Ninject version was that there was no real support for the “Interface segregation principle”. It was not easily possible to tell Ninject that several interfaces shall be bound to the same service so that in case of a singleton the same instance is returned for different interfaces. Ninject provides now a fluent syntax that allows you to bind up to 4 interfaces together to the same instance:
Bind<IInterface1, IInterface2, IInterface3, IInterface4>().To<Implementation>(). ...;
Some may ask what is if I want to bind more than four interface to the same service. In a small discussion we came to the conclusion that if you have more than four interfaces on a single service than most likely you have a problem with the single responsibility principle and should fix this in first place. So I put the limit to four for the fluent syntax because adding additional ones means quite a bit of work in the implementation. For those that really need more interfaces there is a workaround though:
var bindingConfiguration = Bind<IInterface1, IInterface2, IInterface3, IInterface4>() .To<Implementation>() .BindingConfiguration; kernel.AddBinding(new Binding(typeof(IInterface5), bindingConfiguration)); kernel.AddBinding(new Binding(typeof(IInterface6), bindingConfiguration));
Fluent syntax changes
Several changes for the fluent syntax were introduced:
- ToConstructor has been added. See Ninject constructor selection preview for detailed information.
- WhenInjectedInto previously matched only when the target type was exactly the specified one. Now it matches also if the target is derived from the specified type.
- WhenInjectedExactlyInto has been added for all those that want the previous behavior of WhenInjectedInto.
- WhenAnyAnchestorNamed has been added. Other than WhenParentNamed it does not match only if the parent has the specified name but also if any of its ancestors has the specified name.
- OnActivation and OnDeactivation receive now the implementation type instead of the service type as instance. In case the implementation type is undefined an object is passes as instance.
- Generic overloads for OnActivation and OnDeactivation have been added that allow casting the implementation type. This is required in case the implementation type is undefined.
Constructor selection and strong typed constructor arguments
This release introduces the new “To” fluent syntax overload “ToConstructor”. This overload allows you to select the constructor that shall be used in the fluent syntax and to specify constructor arguments strongly typed and without magic strings. For more information about this feature please read my previous blog post about this topic. Ninject constructor selection preview
Furthermore, the default selection of the constructor has been changed. Previous versions of Ninject considered constructors that have self bindable dependencies (e.g. a none abstract class) or dependencies for which a default value (int x = 3) is defined but no binding has been defined for those dependencies as invalid constructors and preferred all other constructors. In 3.0 these kinds of parameters are now treated the same way as if a binding exists for them. This can result in a different behavior of your application in case you have more than one constructor for your classes.
This version also requires that the constructor is uniquely selected. In case there is more than one constructor available with the same weight an activation exception is thrown. In this case you have to select the constructor manually (e.g. by using the new ToConstructor syntax).
The change of the constructor selection changes the behavior of the Entity Framework. The entity framework adds several constructors to the ObjectContext. You have to select the parameterless constructor explicitly. E.g. by adding the following binding:
this.Bind<MyEntities>().ToConstructor(x => new MyEntities());
Inherited constructor arguments
Previously it was possible to pass constructor arguments to the resolved object by passing ConstructorArgument to “Get”. This feature was extended so that it is now possible to pass the constructor arguments to deeper levels too using the following syntax:
kernel.Get<IFoo>(new ConstructorArgument("parameterName", value, true)); kernel.Get<IFoo>(new ConstructorArgument("parameterName", context => LazyEvaluatedValue(context), true));
Changed to client profile and removed InRequestScope
Version 3.0 is now build as client profile library. Since InRequestScope requires a reference to System.Web it was removed from Ninject core. This scope type is now provided by the new Ninject.Web.Common extension which is the base for all web type extensions.
Various small changes
- Support for constructor parameter with a default value has been added. If a constructor parameter has a default value it is used in case there is no explicit binding defined.
- Services that are bound using ToConstant are now in singleton scope by default. This decreases the work required when it is used somewhere.
- Open Generics bindings are now overridable by a closed generic binding of the same generic type.
- Instead of specially handling IKernel internal a default binding for IKernel and IResolutionRoot is added by the kernel during construction. This makes it possible to Rebind them if required.
Small extension changes
- Ninject.Extensions.ContextPreservation: BindInterfaceToBinding has been removed. Use the new Ninject core syntax to bind multiple interfaces to one implementation instead.
- Ninject.Extensions.Xml: InRequestScope requires the additional extension Ninject.Web.Common.Xml.