Dependency Injection using Ninject and PostSharp

April 26, 2016 by C#   PostSharp   Ninject  

Dependency injection is one of the common approaches when dealing with the inversion of control (IoC) design principle and the dependency inversion principle (DIP).

Basically instead of passing to / instantiating in, a concrete object, we rely on abstractions (e.g. an interface).

This loosens the coupling between classes, making our code more readable, reusable, testable, maintainable and allows concurrent / independent development of source code.

I am not going to go into too much detail about its various advantages, this post is more interested in its practical implementation.

Observe the following snippet.

class Products
{
    private IProductRepository _productRepository;

    public Products(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public void SomeMethod()
    {
        _productRepository.GetAll();
    }
}

One of the most common ways of passing dependencies is via constructors (like seen in the snippet above), other obvious options include properties or simply passing it via the method that requires it.

Now instead of instantiating our concrete classes all over the show, we can make use of a dependency injection framework like Ninject to manage our dependencies in a centralized location.

In the following snippet you can see a very basic example of this.

var kernel = new StandardKernel();
kernel.Bind<IProductRepository>().To<ProductRepository>();
var products = kernel.Get<Products>();
products.SomeMethod();

Personally the use of Ninject's Get method feels a bit unnatural to me, so I decided to automate things using a custom aspect I wrote using the PostSharp library.

Subsequently changing the snippet to look like this:

class Products
{
    [Dependency]
    private IProductRepository _productRepository = null;

    public void SomeMethod()
    {
        _productRepository.GetAll();
    }
}

Instead of requesting an instance from Ninject directly, our aspect takes care of it for us.

To achieve this, you will first of all need a Ninject kernel that is accessible to your aspect, like seen below.

static class Dependencies
{
    static StandardKernel _kernel { get; } = new StandardKernel();

    static Dependencies()
    {
        _kernel.Bind<IProductRepository>().To<ProductRepository>();
    }

    public static object Get(Type type, params IParameter[] parameters)
        => _kernel.Get(type, parameters);

    public static object Get<T>(params IParameter[] parameters)
        => _kernel.Get<T>(parameters);
}

Not much to this, a singleton containing the kernel, along with two methods exposing whatever is needed from it.

In the snippet below, I created a custom LocationInterceptionAspect which makes use of the static class in the preceding snippet.

[Serializable]
class DependencyAttribute : LocationInterceptionAspect
{
    public override void OnGetValue(LocationInterceptionArgs args)
    {
        args.Value = args.GetCurrentValue();
        if (args.Value == null)
        {
            args.Value = Dependencies.Get(args.Location.LocationType);
            args.ProceedSetValue();
        }
    }
}

Very funky, now whenever we need to use the repository, the aspect will instantiate it for us using the Ninject bindings specified in our Dependencies class.

My repository might also be of some interest from an AOP point of view, have a look.

class ProductRepository : IProductRepository
{
    [Dummy]
    public List<Product> GetAll()
    {
        return new List<Product> {
            new Product { Name = "Motorized Ice Cream Cone" },
            new Product { Name = "Self-Stirring Mug" },
            new Product { Name = "Battery-Powered Scissors" }
        };
    }
}

In the preceding snippet I make use of dummy data, notice the dummy attribute decorating my GetAll method.

When you attempt to compile the source code to release mode, this source code will purposely fail to compile whenever it finds the Dummy MethodLevelAspect, thereby warning us of any non-production methods we might have forgotten about.

The dummy aspect look like this:

class DummyAttribute : MethodLevelAspect
{
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
#if !DEBUG
        throw new NotImplementedException($"{method.Name}, {method.ReflectedType}");
#endif
    }
}


Well, I am off to Calitzdorp with my girlfriend for some R&R, let me know in the comment section what you think (about the post that is Winking smile)


Leave a Comment


Thanks for the learning! August 18, 2016 by Mr Min

Awesome post thanks!