Click here to Skip to main content
15,867,453 members
Articles / Containers

Dependency Injection to The Core

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
28 Dec 2016CPOL13 min read 24.3K   20   4
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 that 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’ll suggest you follow the official link from asp.net core documentation provided below.

https://docs.asp.net/en/latest/fundamentals/dependency-injection.html

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 up 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 different components in our application is loosely coupled together. DI is a way in which we can achieve loose coupling in our application.

In 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 provides their own way of implementing DI on 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.)

C#
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,

C#
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 a n-tier application.

C#
public class TodoController : ApiController
{
    private readonly TodoRepository _todoRepository = new TodoRepository();
    // GET: api/Todo
    public IEnumerable<Todo> Get()
    {
        return _todoRepository.GetAll();
    }

    // GET: api/Todo/5
    public Todo Get(int id)
    {
        return _todoRepository.Find(id);
    }

    // POST: api/Todo
    public void Post([FromBody]Todo todo)
    {
         _todoRepository.Add(todo);
    }

    // PUT: api/Todo/5
    public void Put(int id, [FromBody]Todo todo)
    {
        _todoRepository.Update(id, todo);
    }

    // DELETE: api/Todo/5
    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 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 requirements and now wants us to read and write comma separated data (CSV) from a filesystem 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, now 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 but 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

C#
public class TodoCSVRepository
{
    private readonly SomeCSVLibary _someCSVLibrary = new SomeCSVLibary();

    public IEnumerable<Todo> GetAll()
    {
        /* Use _someCSVLibrary library instance and get all the todo */
    }

    public void Add(Todo item) 
    {
    	 /* Use _someCSVLibrary library instance to add a new todo */
    }

    public Todo Find(int id)
    {
        /* Use _someCSVLibrary library to find a todo by id */
    }

    public Todo Remove(int id)
    {
        /* Use _someCSVLibrary library to remove a todo by id */
    }

    public void Update(int id, Todo item)
    {
        /* Use _someCSVLibrary library to update a todo */
    }
}

Suppose later our client changed the requirements again and now he/she provided us 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 every time when our client is changing his requirements we are creating a new repository and instantiating that repository in the controller (modifying code, not making the code extensible). So, let’s stop us 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,

C#
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 we can extract an interface (abstraction) out of the repository and use that instead of the concreate 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,

C#
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
C#
public class TodoRepository : ITodoRepository { ... }
TodoCSVRepository.cs
C#
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,

C#
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,

C#
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 of ours then he/she has to pass a new instance of the type of the concreate repository of their like as a parameter in the controller’s constructor.

C#
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 its 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) ,

C#
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.

C#
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,

C#
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,

http://blog.ploeh.dk/2012/09/28/DependencyInjectionandLifetimeManagementwithASP.NETWebAPI/

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,

http://autofac.readthedocs.io/en/latest/integration/webapi.html

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. Time to configure it. I’ve created a class in the App_Start folder and added this method where I’ve configured Autofac like this.

C#
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,

https://github.com/autofac/Autofac

Next, we have to called 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.

C#
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 concreate repositories since we are no longer depend 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 look like this,

C#
public class TestTodoControlloer
{
    [TestMethod]
    public void GetAll_ShouldReturnAllTodos()
    {
        // Arrange
        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);

        // Act
        var todoes = controller.Get();

        // Assert
        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

C#
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,

C#
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 with .net core in this link below,

http://docs.autofac.org/en/latest/integration/aspnetcore.html

You can learn more about dependecy injection in .net core from this official link.

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Geek Hour
Bangladesh Bangladesh
Tech Enthusiast | Contributing Author on Microsoft Docs | Github Country Leader (C# and Typescript)

A .NET and JavaScript enthusiast. Likes to work with different technologies on different platforms. Loves the logic and structure of coding and always strives to write more elegant and efficient code. Passionate about design patterns and applying Software Engineering best practices.

I'm a young coder who did exceedingly well in my education and was offered an internship by Microsoft. I've tried a range of things and realized that what I need is a super creative challenge. I'm hungry for a real role in a challenging startup, something I can stick with for years

Comments and Discussions

 
QuestionMessage Closed Pin
8-May-21 21:14
domain one8-May-21 21:14 
QuestionBasics Pin
Luka4-Oct-17 23:55
Luka4-Oct-17 23:55 
GeneralMy vote of 5 Pin
Imrez Khan Ratin3-Jan-17 0:58
Imrez Khan Ratin3-Jan-17 0:58 
GeneralThanks Pin
Member 1292842930-Dec-16 3:39
professionalMember 1292842930-Dec-16 3:39 
Very good article! Thanks
GeneralRe: Thanks Pin
Fiyaz Hasan30-Dec-16 6:52
professionalFiyaz Hasan30-Dec-16 6:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.