Easying DI registration Using AutoFac Assembly Scanning

In today’s blog, we are going to talk about how we can leverage the Autofac feature of assembly scanning to ease the Dependency Registration process.

Conventional DI process

.NetCore has an inbuilt mechanism for specifying Dependency Injection in our application. To start with, the Startup.cs class has 2 main methods i.e ConfigureServices and Configure. The ConfigureServices as the name says, is the method that takes in the configuration about all the services required to consume. The Configure method is a container, that defines middleware components used in request pipeline, which will be executed on every request.

Commonly, we use Constructor Injection, where we define the list of required Dependencies by specifying them as parameters to the class’s constructor. Once defined, we make a field of that interface. With this field, we can call the interface methods.

  • Registering services in ConfigureServices method
public void ConfigureServices(IServiceCollection services)
		{
			services.AddSingleton<IEmployee, EmployeeService>();
			services.AddSingleton<IDesignation, DesignationService>();
			services.AddControllersWithViews();
		}

It is as easy as it looks in the code snippet. But this approach has 2 major drawbacks,

  1. Lengthy Startup class: The lines for registering services will keep on increasing with the expansion of the project. Each service class, you introduce and decorate with business logic, will have to be registered here for consideration in the DI process.
  2. Missed Dependencies: With so many lines, there is a possibility of missing the registration of service.

Objective/Solution

Our objective here would be to overcome the aforementioned problems using Autofac. So let’s jump to the project and find it by ourself.

Project Structure

Project Structure
Project Structure

Our project would have the following structure where,

  1. DIRegistration, is our class library. The responsibility of this library will be to contain classes that will do Dependency registration in our application.
  2. DependencyRegistrationUtil, is an utility Class. We will call this class from our project, whose services we wish to register. This class will take ContainerBuilder and Assembly as parameters.
namespace DIRegistration
{
	public class DependencyRegistrationUtil
	{
		public static void RegisterSingleInstance(ContainerBuilder builder, Assembly assembly)
		{
		builder.RegisterAssemblyTypes(assembly).Where(t => typeof(IDependencyRegistrable).IsAssignableFrom(t))
			.AsImplementedInterfaces().SingleInstance();
		}
	}
}
  • IDependencyRegistrable, is our markup interface used for recognizing dependencies which should be registered automatically.
  • AutofacDIModule, will inherit from Module provided by Autofac and we would override Load() method to call our DependencyRegistrationUtil.
namespace DependencyRegistration
{
    public class AutofacDIModule : Module
    {
        public AutofacDIModule()
        {
        }
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);
            DependencyRegistrationUtil.RegisterSingleInstance(builder, ThisAssembly);
        }
    }
}

Usage

  • Let’s first register our AutofacDIModule in StartUp.cs class.
public void ConfigureContainer(ContainerBuilder builder)
		{
			builder.RegisterModule<AutofacDIModule>();
	}
  • EmployeeService.cs is our Business layer class, which we would call from Controller through DI. This class will implement our markup interface, IDependencyRegistrable, along with other service related interfaces.
namespace DependencyRegistration.Services
{
	public class EmployeeService : IEmployee,IDependencyRegistrable
	{
		public async Task<string> GetEmployeeName()
		{
			return Task.Run(() => "Michael").Result;
		}
	}
}
  • With this step up, the services are now ready to be consumed.
namespace DependencyRegistration.Controllers
{
	public class HomeController : Controller
	{
		private readonly IDesignation _designation;
		private readonly IEmployee _employee;
		public HomeController(IDesignation student, IEmployee employee)
		{
			_designation = student;
			_employee = employee;
		}
		public async Task<IActionResult> Index()
		{
			ViewData["message"]=$"{await _employee.GetEmployeeName()} is a {await _designation.GetDesignation() }";
			return View();
		}
	}
}

This mechanism removes the overhead of registering a class when a class is added or removed. The code is also easy to maintain. There are also ways, with which you can differentiate the type of registration i.e Scoped, Transient and Singleton in above mechanism, for which I would write another blog.

Code smart!

Feel free to access full sample code from here