Learn how dependency injection mechanism has evolved from ASP.NET to ASP.NET Core.
Okay ! Since I’m writing blogs mostly on ASP.NET Core lately, you might be wondering why am I writing a blog on a topic which is already available in ASP.NET Core official documentation.
Well, in this post, I’m not only going to talk about how you can achieve DI (Dependency Injection) in .NET Core but will also discuss about DI itself (what, why and how). This article reflects how I actually learned (still learning) DI. So, if you don’t want to follow my approach or don’t want a deep dive into DI, then I would suggest you to follow the official link.
If you are reading this paragraph, then maybe you are interested in a deep dive into the wonderful world of DI with me. So, are you ready? I guess you are. Let’s get started!
An application is made of different components coupled together. Coupling is good but what about strict coupling? That’s definitely bad. According to the second principle of the very well-known software design principles (simply called SOLID), "Software entities (components/class/module/functions) should be open for extension, but closed for modifications (ca be achieved with abstract classes/interfaces)."
Since we need to make our application extensible over time, we need to make sure that different components in our application are loosely coupled together. DI is a way in which we can achieve loose coupling in our application.
On Enterprise level, we often work with n-tier applications. For our example, let’s assume that we have this 3-tier application.
It is basically a Web API application. So, all I did is I exposed some data over the client (presentation layer) using APIs. Here, we have AngularJS on the client-side (presentation layer). We have our API Controllers in the business layer and we have our Entity Framework database context to do the dirty works in the data access layer.
In this case, we really don’t have to worry about DI in the presentation layer because we can do DI in client side separately. Frameworks like AngularJS provide their own way of implementing DI, client-side. Notice that I said frameworks (AngularJS, EmberJS, BackboneJS), not libraries (jQuery, Knockout). You can use libraries and make your own framework where people can implement DI in your provided way.
I’m making things clear so that you can’t poke me later saying, “you didn’t do DI in the presentation layer”. DI is really just a concept and a technique to learn, upon learning, you can use the same concept and technique in any kind of application framework of your like. This can be either server side framework or client-side framework.
So, don’t mix things up.
Today, I’ll show you how you can achieve dependency in ASP.NET Core. So, that’s server-side. But before that, let’s see what is the current state of our application. Let’s take a look into the business layer and data access layer.
A snapshot from the data access layer would be (don’t try to understand the code.)
- public class TodoRepository
- {
- private readonly TodoContext _context = new TodoContext();
-
- public IEnumerable<Todo> GetAll()
- {
- return _context.Todos;
- }
-
- public void Add(Todo item)
- {
- _context.Todos.Add(item);
- _context.SaveChanges();
- }
-
- public Todo Find(int id)
- {
- Todo todo = _context.Todos.AsNoTracking().FirstOrDefault(t => t.Id == id);
- return todo;
- }
-
- public Todo Remove(int id)
- {
- Todo todo = _context.Todos.FirstOrDefault(t => t.Id == id);
- _context.Todos.Remove(todo);
- _context.SaveChanges();
-
- return todo;
- }
-
- public void Update(int id, Todo item)
- {
- Todo todo = _context.Todos.FirstOrDefault(t => t.Id == id);
- todo.Title = item.Title;
- todo.IsDone = item.IsDone;
- _context.SaveChanges();
- }
- }
Notice that here, we are creating a new instance of TodoContext every time we are calling the TodoRepository class. Our TodoRepository is working as a wrapper which eventually talks to a specific DbSet defined in the TodoContext. Here is how our TodoContext looks like.
- class TodoContext : DbContext
- {
- public TodoContext() : base("TodoDbConnectionString")
- {
-
- }
- public DbSet<Todo> Todos { get; set; }
- }
N.B
TodoDbConnectionString is the connection string name.
We have the API Controller in the business layer (middle layer). If you are still trying to understand the code, then don’t. We are here to learn DI not how to make an n-tier application.
- public class TodoController : ApiController
- {
- private readonly TodoRepository _todoRepository = new TodoRepository();
-
- public IEnumerable<Todo> Get()
- {
- return _todoRepository.GetAll();
- }
-
-
- public Todo Get(int id)
- {
- return _todoRepository.Find(id);
- }
-
-
- public void Post([FromBody]Todo todo)
- {
- _todoRepository.Add(todo);
- }
-
-
- public void Put(int id, [FromBody]Todo todo)
- {
- _todoRepository.Update(id, todo);
- }
-
-
- public void Delete(int id)
- {
- _todoRepository.Remove(id);
- }
- }
Notice that, here again, we are creating a new instance of TodoRepository every time we are calling the API Controller. Always remember that creating a new instance means gluing stuff together. And that’s exactly what we are doing in the TodoController and TodoRepository class. What if we change our mind and want to use another repository (for example, a repository that can do CRUD operations against a file system) in the Controller instead of the current one? Again, what if we want to use a different data access library in the repository instead of the current one. Here’s what we will do when no one is watching.
We will go to those individual classes and then replace the instantiations with the new types. But doing this will break the second rule of the SOLID design principle. (Here, not only we are making our application inextensible but we are also modifying it each time our requirement is changing).
Say for example, our client changed their requirement and now wants us to read and write comma separated data (CSV) from a file system instead of a database. Suppose, I’ve searched and downloaded a cool library that can work with CSV files. So, we have a CSV library configured and ready to talk with our CSV files in the data access layer.
If you are following me, then you would say that we have a new data access layer. We also need a new repository. Right? Exactly! Since we want to do the exact CRUD operations, now against a CSV file, our method signatures will be the same as we have for the TodoRepository but the implementation will be different (because now we are using a new library instead of EF to talk to a CSV file).
Let us not go into the implementation details; rather, just create the repository and declare the methods. Let’s say, the name of our new repository is TodoCSVRepository.
- public class TodoCSVRepository
- {
- private readonly SomeCSVLibary _someCSVLibrary = new SomeCSVLibary();
-
- public IEnumerable<Todo> GetAll()
- {
-
- }
-
- public void Add(Todo item)
- {
-
- }
-
- public Todo Find(int id)
- {
-
- }
-
- public Todo Remove(int id)
- {
-
- }
-
- public void Update(int id, Todo item)
- {
-
- }
- }
Suppose, later our client changed the requirement again and now he/she provided us with a new library which can talk to an in-memory database system. So, we require another repository (TodoInMemoryepository). But wait! Don’t you think every time when we are creating a new repository, we are declaring the same sets of methods which do some CRUD operations against our preferred data storage?
That’s not a big issue here but the issue is that every time when our client is changing the requirement, we are creating a new repository and instantiating that repository in the Controller (modifying code, not making the code extensible). So, let’s stop from modifying the Controller code once and for all. We have to take care of this culprit line which is new-ing up a repository.
- private readonly TodoRepository _todoRepository = new TodoRepository();
We can solve this problem of ours easily by creating an abstraction for our repositories. What I meant is that we can extract an interface (abstraction) out of the repository and use that instead of the concrete type of a preferred repository. Since every repository has the exact sets of methods, we can extract an interface out of them which basically looks like this.
- public interface ITodoRepository
- {
- void Add(Todo item);
- Todo Find(int id);
- IEnumerable<Todo> GetAll();
- Todo Remove(int id);
- void Update(int id, Todo item);
- }
We can now make the repositories implement that simple interface (abstraction) like this.
TodoRepository.cs - public class TodoRepository : ITodoRepository { ... }
TodoCSVRepository.cs - public class TodoCSVRepository : ITodoRepository { ... }
With this simple abstraction of ours now we can modify the controller code and make it depend on an abstraction rather than a concreate type. Remember that culprit of ours? It should now look like this,
- private readonly ITodoRepository _todoRepository = new TodoRepository();
Not done yet! We are still new-ing up a specific concreate repository. You can’t create an instance of a interface so still we are dependent on a specific repository. Why don’t we modify further? Let’s create an ITodoRepository field and instantiate it from the controller like this,
- private readonly ITodoRepository _todoRepository;
-
- public TodoController()
- {
- _todoRepository = new TodoRepository();
- }
As you can see now that our Controller's constructor is dealing with the task of creating a concrete type but we can delegate this task of creating a concreate type to someone else who will call this Controller of ours. What I meant is, if someone wants to use this controller, then he/she has to pass a new instance of the type of the concrete repository of their like as a parameter in the Controller’s constructor.
- private readonly ITodoRepository _todoRepository;
-
- public TodoController(ITodoRepository todoRepository)
- {
- _todoRepository = todoRepository;
- }
In this way, we can pass the responsibility upward and make our controller safe from further code modification. This kind of design (passing the type of instance through constructor from somewhere else) is also known as the constructor injection pattern.
This also follows the fifth principle (Dependency Inversion Principle) of SOLID design principles which states that,
"High-level modules should not depend on low-level modules. Both should depend on abstractions." Everything looks good but at this point, your program won’t run as you expected because it's expecting a instance of a repository type to be passed in the Controller. So, we can do some pure man’s DI here (passing an instance of an concrete repository type from the default constructor) ,
- private readonly ITodoRepository _todoRepository;
-
- public TodoController() : this(new TodoRepository())
- {
-
- }
-
- public TodoController(ITodoRepository todoRepository)
- {
- _todoRepository = todoRepository;
- }
Still we have to modify the controller code when we want to use a different repository. But we can go further and build a composition root where we can do this kind of type initializations. The composition root is a simple class which will get called when the application is first initialized. In that class, we can resolve the specific types for our abstractions.
- public class CompositionRoot : IHttpControllerActivator
- {
- public IHttpController Create(
- HttpRequestMessage request,
- HttpControllerDescriptor controllerDescriptor,
- Type controllerType)
- {
- if (controllerType == typeof(TodoController))
- return new TodoController(
- new TodoRepository());
-
- return null;
- }
- }
As you can see here, we have configured our HTTPRequest in such a way that if someone requests the TodoController we will instantiate a new instance of TodoRepository and pass it to the controller's constructor. Likewise, we can change it to TodoCSVRepository or TodoInMemoryepository whenever we feel like. Now we have a single place to do all our dirty works of type initializations. In Web API projects we have to register this composition root in the Global.asax.cs file like this,
- GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),new CompositionRoot());
One thing to remember here is, a composition root implementation varies from framework to framework. Here we are working with Web API so this won’t work for MVC (may work) and WPF applications. You have to find a way to implement those but don’t worry because the internet has a lot of code snippets that can ease up your task of creating a composition root for a specific framework. Tell you what, I learned how to make a composition root like this for Web API projects after reading a blog from Mark Seeman. Here is the
link for it,
That’s good since we have to deal with resolving tiny dependencies for this small project of ours. What if we have a large project where hundreds of dependencies are scattered around? In that cases, composition root won’t be a good idea. That is why in enterprise level, we use a well-known IoC (Inversion of Control) container to make our job easy. IoC containers can resolve dependencies recursively and they are also pretty much easy to configure. They allows us to work with dependency injection lifecycle easily.
There are many IoC containers available and most of them do the same things somewhat differently. Let’s use one of them in our current project. Let’s pick Autofac which has a great documentation online. Here is the
link where you can know all about the Autofac integration related stuff with Web API projects,
Since we are just beginners wondering around the world of dependency injection, we will go slow and easy. The Autofac library for Web API projects is available to download to from NuGet.
Install-Package Autofac.WebApi2 I’ve downloaded it in my Techtalkers.WEB project. It is time to configure it. I’ve created a class in the App_Startfolder and added this method where I’ve configured Autofac like this.
- public class AutofacConfig
- {
- public static void RegisterAutofac()
- {
- var builder = new ContainerBuilder();
-
- var config = GlobalConfiguration.Configuration;
- builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
- builder.RegisterType<TodoRepository>().As<ITodoRepository>().InstancePerRequest();
-
- var container = builder.Build();
- config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
- }
- }
Here, we get the HttpConfiguration which is basically the GlobalConfiguration.Configuration. Registered all the Api controllers available in the current assembly (Techtalkers.WEB). Then registered the TodoRepository so that a new instance of it can be served whenever we have ITodoRepository in the request scope. Notice that we can define lifetime for a specific instance.
Here, InstancePerRequest() will create a new singleton instance of TodoRepository per request and shared across the nested parentand nested scopes. You can define different lifetimes for your service instance just by using the other extension methods available from Autofac. And this is actually a great functionality available in all the Ioc containers.
Lastly, we have built the container and passed it in the AutofacWebApiDependencyResolver. This class basically implements the IDependencyResolver interface provided by the Web API project. That’s it. From now on Autofac will resolve all the dependencies for our project. We don’t have to bother about how it will do it. Thing is it will do it. And if you are interested in the raw implementation then you can check the github repository for Autofac. This is an open-source project. Here is the github repository
link,
Next, we have to call the RegisterAutofac() from the Global.asax.cs. There, the Application_Start() is called every time the application is started. So, like the other registration stuff, I’ve registered the RegisterAutofac() method there.
- protected void Application_Start()
- {
- AreaRegistration.RegisterAllAreas();
- GlobalConfiguration.Configure(WebApiConfig.Register);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteConfig.RegisterRoutes(RouteTable.Routes);
-
- AutofacConfig.RegisterAutofac();
- }
Now, we are in a situation where we can easily test our TodoController. We don’t have to worry about instantiations of concrete repositories since we are no longer dependent on any of them. Instead, we can use a mock repository whenever we are in the test. A library called Moq can help us do that. A simple test for the Get()method will looks like this,
- public class TestTodoControlloer
- {
- [TestMethod]
- public void GetAll_ShouldReturnAllTodos()
- {
-
- var mockRepository = new Mock<ITodoRepository>();
-
- mockRepository.Setup(x => x.GetAll())
- .Returns(new List<Todo>()
- {
- new Todo() {Id = 1, Title = "Test Item 1", IsDone = true},
- new Todo() {Id = 2, Title = "Test Item 2", IsDone = true},
- new Todo() {Id = 3, Title = "Test Item 3", IsDone = false}
- });
-
- var controller = new TodoController(mockRepository.Object);
-
-
- var todoes = controller.Get();
-
-
- Assert.IsNotNull(todoes);
- Assert.AreEqual(3, todoes.Count());
- Assert.AreEqual(2, todoes.Count(t => t.IsDone));
- }
- }
As you can see here, we don’t have to create a concrete repository type to pass it around in the controller. We can just simply use a mock object instead.
Now that we have some get to go understanding on DI, we can make a tour to the wonderful world of .net core and see how DI is done there. .Net core by default provides some bare minimum functionality to do DI. But most of the time it’s enough for even big project that has hundreds of dependencies. For the time being .net core’s default IoC container only support construction injection.
So, just like we did with Autofac if you want to serve a concrete TodoRepository instance whenever ITodoRepository get caught in the scope, you would do something like this in the ConfigureServices()method,
- services.AddScoped<TodoRepository, ITodoRepository>();
As you can see, we can define instance lifetime at the same time. Here, defining a service in Scoped lifetime means that a new instance of TodoRepository will be created per request and it will be shared in nested scopes.
The two other service lifetimes are Singleton and Transient. And they can be registered like above,
- services.AddSingleton<TodoRepository, ITodoRepository >();
- services.AddTransient<TodoRepository, ITodoRepository >();
In singleton lifetime, service instances are created the first time they are requested and in subsequent requests the instances will be shared though all the parent and nested scopes.
In transient lifetime, new service instances are created per requests for all the parent and nested scopes.
If you dont feel happy with the built-in functionality, you can also add third part IoC containers. Autofac, by itself, can be used with .NET Core. You can have a good idea on how to integrate Autofac at this
link.
You can learn more about dependecy injection in .net core from this official
link.
So, that’s it. I guess now you are pretty much comfortable with dependency injection and its related terms. But before you think you are done, let me remind you again that these are very much beginner level articles. Also, I only talked about constructor injection. But there is also property injection, method injection, interface injection and etc. I’ll let you do the honor and explore them. Other than lifetime management, you can do a lot with Ioc containers, so there are lot of things to learn too. Now, go and get your feet wet.