Dependency Injection with Autofac
Dependencies Injection is a pattern which could be very helpful to create a maintainable and a less complex code. It is a form of IoC (Inversion of Control). The aim of this pattern is to give away the responsibility to instance some objects within own code. That means we don't need to take care of, how, when and where we create an instance of an object. Also you have the possibility to mock up the interfaces and services in order to create unit tests. If you once understand it, you will never want to code without it again.
There are three ways to inject the dependencies (Constructor Injection, Property Injection, Method Injection), in this article we will talk about the Constructor Injection.
In Sitecore projects using MVC pattern, usually we implement the business functions in services, which will be used by the controllers of the renderings. Instead of creating instances of those services within the Controllers, we could simply "inject" there dependencies.
Within this article we will create a small web application in ASP.Net MVC, this application should store some users in the database after validating its content.
To store the user objects we will use the Entity Framework (EF6, Model First approach).
1. Create ASP.NET MCV Solution
Open Visual Studio (in this example VS2017), create a new Project by selecting "ASP.NET Web Application (.NET Framework)
After that, we define our model structure, we create first a class named "UserGroup.cs".
namespace DependencyInjection.Models
{
public class UserGroup
{
public string Name { get; set; }
public string Description { get; set; }
}
}
We create also a class named "User.cs"
using System;
namespace DependencyInjection.Models
{
public class User
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
public string Adress { get; set; }
public UserGroup UserGroup { get; set; }
}
}
To store users in the database, we add a new project item "ADO.NET Entity DataModel" and we call it "DataContext", after adding this item, a new class "DataContext.cs" will be generated. Within this class we add a property of type DbSet to query our users from it. For more informations about Entity Framework see: https://msdn.microsoft.com/en-us/library/jj193542(v=vs.113).aspx
Now we implement two services. DatabaseService to handle all operations against the database or in this case our DataContext, ValidationService will validate all created users before storing in database. Each service will implement it is own interface (IDatabaseService, IValidationService).
using DependencyInjection.Models;
namespace DependencyInjection.Business
{
public interface IDatabaseService
{
bool AddUser(User user);
bool UpdateUser(User user);
bool RemoveUser(User user);
}
}
So we create a class "DatabaseService" implementing from IDatabaseService interface. This service will be the only class which interact with the DataContext, to achieve that we need to instance the DataContext class, or we inject it into DatabaseService and we give the responsibility to instance it to another class or framework.
using System.Data.Entity;
using DependencyInjection.Models;
namespace DependencyInjection.Business
{
public class DatabaseService:IDatabaseService
{
private readonly DataContext _dataContext;
public DatabaseService(DataContext dataContext)
{
_dataContext = dataContext;
}
public bool AddUser(User user)
{
_dataContext.Users.Add(user);
return _dataContext.SaveChanges().Equals(1);
}
public bool UpdateUser(User user)
{
_dataContext.Entry(user).State = EntityState.Modified;
return _dataContext.SaveChanges().Equals(1);
}
public bool RemoveUser(User user)
{
_dataContext.Entry(user).State = EntityState.Deleted;
return _dataContext.SaveChanges().Equals(1);
}
}
}
The constructor of DatabaseService receive a parameter of type DataContext, after passing this parameter to a private property, we can use the functions of DataContext in the whole DatabaseService class. We did nothing else as "injecting" the dependency of DataContext to DatabaseService throw its constructor. DatabaseService don't need to instance DataContext to use its functions.
Now we implement the "ValidationService.cs" which implement the interface "IValidationService",
public interface IValidationService
{
bool UserIsValid(User user);
}
public class ValidationService:IValidationService
{
public bool UserIsValid(User user)
{
return !string.IsNullOrEmpty(user.Name)
&& !string.IsNullOrEmpty(user.Adress)
&& user.UserGroup != null;
}
}
But we are not finished yet, the DataContext should be instanced by a class or framework, Dependency Injection doesn't mean we can use uninstantiated classes.
2. Using Autofac
Autofac is a framework for Microsoft .NET to build container to inject constructor with parameters and to manage dependencies between classes. It is able to instance services/controllers and classes.
To Install the Autofac Nuget Package using Package Manager Console in Visual Studio, enter "Install-Package Autofac.Mvc5 -Version 4.0.2"
or open the Nuget Package Manager Tool and search for Autofac.Mvc (see: https://www.nuget.org/packages/Autofac.Mvc5/4.0.2)
In the next step we need to tell Autofac which services and classes we want to inject in the application. We could create a static class "DependencyRegister.cs" which contains the steps to register the services and classes. Please note that the registration should be executed before calling the first constructor with injected parameter , the best way is to execute the registration within the Global.asax class.
We take a look at the the implementation of our "DependencyRegister.cs" class
public static class DependencyRegister
{
public static void ExecuteRegistration()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
As we can see, after creating an instance of ContainerBuilder(), we need to register the controller which also could have injected parameter to their constructors, with Autofac we are able to register all Controllers within the application with no need to specify each one of them.
In the next lines we register the DatabaseService, ValidationService and their interfaces IDatabaseService and DatabaseService.
In "DatabaseService" we used "DataContext.cs" as injected instance, so we need also to register it.
builder.RegisterType();
After that we build the registered types and create a container.
var container = builder.Build();
At the end we resolve the dependencies created within the container.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
Now we have registered all our classes and services needed and we can use it in any class if we pass its instances as parameter to the constructor.
As additionally example, we want to provide the possibility to validate and to add an user within the "Home" Controller. So we define an instance of the interface IDatabaseService and an instance of IValidationService as parameter for the constructor of "Home" Controller.
namespace DependencyInjection.Controllers
{
public class HomeController : Controller
{
private readonly IDatabaseService _databaseService;
private readonly IValidationService _validationService;
public HomeController(IDatabaseService databaseService, IValidationService validationService)
{
_databaseService = databaseService;
_validationService = validationService;
}
[HttpPost]
public ActionResult Create(User user)
{
if (!_validationService.UserIsValid(user))
return View("Error");
_databaseService.AddUser(user);
return View("Index");
}
public ActionResult Index()
{
return View();
}
}
}
We have implemented a small web application with two services without creating any instance of it, creating instances is now the concern of Autofac.