Doc/DependencyInjection/Ninject

Using Ninject as the IoC Container

OpenRasta uses built-in dependency injection, however it is implemented using an abstraction which can be implemented by any other container. The "ninject" module (OpenRasta.DI.Ninject.dll) is the Ninject 2.0 implementation of OpenRasta's DI abstraction.

Container Requirements

OpenRasta is built with several requirements about how the DI container should behave that differ from out-of-the-box Ninject behavior:

  1. Constructor selection
    • A constructor with registered arguments will be favored over a constructor that has arguments that aren't registered. (Ninject default behavior is to select the constructor with the most arguments, regardless of whether they were registered (bound) or not.)
  2. Property injection
    • A property of a registered type will be injected, and will be ignored if the type isn't registered. (Ninject only injects properties decorated with a specific attribute.)
  3. Custom "per-request" scope
    • Because it can be hosted in many environments, OpenRasta's per-request lifetime is a custom implementation. (Ninject's default per-request scope uses the HttpContext?.)

Because of these non-standard requirements, the NinjectDependencyResolver? has a static factory method that will create a pre-configured Ninject kernel:

IKernel kernel = NinjectDependencyResolver.CreateKernel();

An overload also exists that accepts a "parent" kernel (see below for more information):

IKernel kernel = NinjectDependencyResolver.CreateKernel(parentKernel);

Configuring OpenRasta to use Ninject

Examples of how to use OpenRasta with Ninject follow. The examples are not complete; they only illustrate the steps specifically required to support Ninject. Other areas of this documentation describe how to set up and configure OpenRasta, and the examples below should be used in the appropriate places.

Step 1: Implement the IDependencyResolverAccessor interface

This class is passed to the hosting environment and enables OpenRasta to retrieve the IDependencyResolver (the container). The factory is responsible for ensuring only a single instance is created, hence the private static member. (The public property is part of the IDependencyResolver interface, so it can't be marked static.)

public class NinjectResolverFactory : IDependencyResolverAccessor
{
    public IDependencyResolver Resolver { get { return _resolver; } }
    private static IDependencyResolver _resolver { get; set; }

    public NinjectResolverFactory()
    {
        if(_resolver == null)
        {
            IKernel kernel = NinjectDependencyResolver.CreateKernel();
            _resolver = new NinjectDependencyResolver(kernel);
        }
    }
}

Step 2: Tell the host about the resolver accessor

You should add this code to the beginning of your OpenRasta Configuration class prior to the using(OpenRastaConfiguration.Manual) statement. The configuration will vary for each hosting environment. Please add examples here as other implementations occur.

HttpListenerHost:

var host = new HttpListenerHost();
host.Initialize(new[] { "http://+:80/" }, "/", typeof(NinjectResolverFactory));

AspNetHost:

var host = new AspNetHost();
host.DependencyResolverAccessor = new NinjectResolverFactory();

That's it! OpenRasta will now use Ninject for its dependency injection. Discussion of using your own types is provided elsewhere in this article.

SubContainer? Kernel

Because of the changes to the default Ninject kernel that OpenRasta requires, it can be undesirable to use the same kernel for registering and resolving your application types. For example, if you are integrating into a legacy application that does not share the same requirements that OpenRasta does. In this case, support exists for using a "sub-container" kernel configuration. Using the sub-container kernel configuration, OpenRasta will attempt to resolve all types out of the "primary" Ninject kernel. If a type is not found, rather than failing, it will request the type from a "parent" kernel and, if found, use that reference. The parent kernel can be configured in any way you like (for example, constructor scoring or property injection, etc.)

This essentially provides a one-way lookup for additional types. It is one-way because the parent kernel cannot resolve references that are registered in the "subcontainer" kernel.

The implementation is exactly the same, except that the static call to "CreateKernel?()" takes a parameter which is the parent kernel to use:

public class NinjectResolverFactory : IDependencyResolverAccessor
{
    public IDependencyResolver Resolver { get { return _resolver; } }
    private static IDependencyResolver _resolver { get; set; }

    public NinjectResolverFactory()
    {
        if(_resolver == null)
        {
            // For example, a static service locator call could be used to retrieve the parent kernel out of the ServiceLocator
            IKernel kernel = NinjectDependencyResolver.CreateKernel(ServiceLocator.Current.GetInstance<IKernel>());
            _resolver = new NinjectDependencyResolver(kernel);
        }
    }
}

In this configuration, a request for type "IRepository<User>" (which is registered in the parent kernel) will be resolved successfully in the OpenRasta handler below:

using OpenRasta.Web;

namespace OpenRasta.Docs.Handlers
{
    public class HomeHandler
    {
        private readonly IRepository<User> _repository;

        public HomeHandler(IRepository<User> repository)
        {
            _repository = repository;
        }

        public object Get() { /* use _repository here */ }
    }
}

Using Custom Types

OpenRasta will automatically register its own standard types like IRequest so that these are available even after you switch to Ninject. Use of Ninject is completely transparent in that respect.

If you desire to mix your types into Ninject's kernel, simply load your modules when the kernel is created (or gain access to it later, appropriately, and load your modules as needed). You can also register your types through OpenRasta's CustomDependency<T> method. This will provide the same automatic dependency injection that OpenRasta provides through its native injection system. This means injection is automatic only for object instances which are directly created by OpenRasta (and their dependencies, of course).

However, you can also take advantage of the many other forms of dependency injection supported by Ninject. To accomplish dependency injection in object instances not created by OpenRasta, you must register the type and the implementation with the kernel (a call to CustomDependency<T> is adequate although Ninject's Bind methods permit a variety of options). Once the type is registered, request instances from the kernel (which will be created or maintained according to the lifetime you specify). Critically, you do not create the object instance yourself with the new keyword (or whatever creation method your .NET language normally uses).

Gaining access to the Ninject kernel is simple: declare a dependency on IKernel. The kernel contains special code to automatically resolve any IKernel requests without requiring you to register the IKernel type. You can use any of the regular dependency declarations -- properties, constructors, etc. This example retrieves the kernel using property injection:

[Inject]
public IKernel kernel { get; set; }

Since OpenRasta automatically performs dependency resolution on objects it creates, you usually don't need the [Inject] attribute since you probably want OpenRasta to automatically supply the initial injection (assuming you use this approach). However, the [Inject] attribute doesn't have any negative side effects, so you can safely decorate all dependencies with it and improve the readability of your code -- and simultaneously eliminate any need to stop and think about whether you or OpenRasta are going to create that instance.

Once you have the kernel reference, you can ask it for any registered type. In OpenRasta you often write a method inside a Handler class which returns an instance of a Resource class. Since OpenRasta creates the Handler, it performs dependency injection automatically for any dependencies declared in the Handler. But since you have to create the Resource class, dependency injection is not automatic: that process is an obvious candidate for leveraging Ninject:

public class MyHandler
{
    private IKernel _kernel { get; set; }

    [Inject]
    public MyHandler(IKernel kernel)
    {
        _kernel = kernel;
    }

    public object Get()
    {
        // without Ninject: return new MyResource();
        return _kernel.Get<IMyResource>();
    }
}

In this case we're getting the kernel reference through constructor injection. We could just as easily get the Resource through the constructor at the same time. When OpenRasta calls the Get() method, we ask the kernel for a concrete object instance that matches our Resource interface. Naturally this assumes that IMyResource was registered through an earlier call to CustomDependency<T> or one of the Ninject Bind methods. In this fashion, every dependency in your Resource class and lower will be automatically resolved by Ninject before the instance is returned from your Handler.