Dependency resolver is used to avoid tightly-coupled classes, improve flexibility and make testing easy. You can create your own dependency injector (not recomended) or use one of well-written and tested dependency injectors. In this example I am going to use Ninject.
Step one: Create dependency resolver.
First of all, download Ninject from NuGet. Create folder named Infrastructure and add class named NinjectDependencyResolver:
using Ninject;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
public class NinjectDependencyResolver
: IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
// Initialize kernel and add bindings
kernel = new StandardKernel();
AddBindings();
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
private void AddBindings()
{
// Bindings added here
}
}
The MVC Framework will call the GetService and GetServices methods when it needs an insance of a class to service an incoming request.
Step two: Register dependency resolver.
Now we have our custom dependency resolver and we need to register it in order to tell MVC framework to use our dependency resolver. Register dependency resolver in Global.asax.cs file:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
DependencyResolver.SetResolver(new NinjectDependencyResolver());
// .....
}
Step three: Add bindings.
Imagine that we have following interface and implentation:
public interface ICustomCache
{
string Info { get; }
}
public class CustomCache : ICustomCache
{
public string Info
{
get
{
return "Hello from CustomCache.";
}
}
}
If we want to use CustomCache in our controller without tightly-coupling our controller with CustomCache, then we need to bind ICustomCache to CustomCache and inject it using Ninject. First things first, bind ICustomCache to CustomCache by adding following code to AddBindings() method of NinjectDependencyResolver:
private void AddBindings()
{
// Bindings added here
kernel.Bind<ICustomCache>().To<CustomCache>();
}
Then prepare your controller for injection as below:
public class HomeController : Controller
{
private ICustomCache CustomCache { get; set; }
public HomeController(ICustomCache customCacheParam)
{
if (customCacheParam == null)
throw new ArgumentNullException(nameof(customCacheParam));
CustomCache = customCacheParam;
}
public ActionResult Index()
{
// cacheInfo: "Hello from CustomCache."
string cacheInfo = CustomCache.Info;
return View();
}
}
This is example of costructor injection and it is one form of dependency injection. As you see, our Home controller does not depend on CustomCache class itslef. If we want to use another inplementation of ICustomCache in our application, the only thing we need to change is to binding ICustomCache to another implentation and that is the only step we need to take. What happened here is, MVC Framework asked our registered dependency resolver to create instance of HomeController class via GetService method. GetService method ask Ninject kernel to create requested object and Ninject kernel examines the type in its term and finds out that constructor of HomeController requeires an ICustomCache and binding has already been added for ICustomCache. Ninject creates instance of binded class, uses it to create HomeController and returns it MVC Framework.
Dependency chains.
When Ninject tries to create type, it examines other depenencies between type and other types and if there is any Ninject tries to create them also. For example, if our CustomCache class requires ICacheKeyProvider and if bining added for ICacheKeyProvider Ninject can provide it for our class.
ICacheKeyProvider interface and SimpleCacheKeyProvider implentation:
public interface ICacheKeyProvider
{
string GenerateKey(Type type);
}
public class SimpleCacheKeyProvider
: ICacheKeyProvider
{
public string GenerateKey(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return string.Format("{0}CacheKey", type.Name);
}
}
Modified CustomCache class
public class CustomCache : ICustomCache
{
private ICacheKeyProvider CacheKeyProvider { get; set; }
public CustomCache(ICacheKeyProvider keyProviderParam)
{
if (keyProviderParam == null)
throw new ArgumentNullException(nameof(keyProviderParam));
CacheKeyProvider = keyProviderParam;
}
...........
}
Add binding for ICacheKeyProvider:
private void AddBindings()
{
// Bindings added here
kernel.Bind<ICustomCache>().To<CustomCache>();
kernel.Bind<ICacheKeyProvider>().To<SimpleCacheKeyProvider>();
}
Now when we navigate to HomeController Ninject creates instance of SimpleCacheKeyProvider uses it to create CustomCache and uses CustomCache instance to create HomeController.
Ninject has number of great features like chained dependency injection and you should examine them if you want to use Ninject.