Dependency Injection in ASP.NET MVC Tutorial

March 07, 2017
6 min read
Dependency Injection in ASP.NET MVC pc screenshot
Introduction

The scope of this tutorial is to unravel (hopefully) some of the mistery behind Dependency Injection, a widely used pattern, that although its implementation may seem complicated (and it may very well be), the concept on the other hand is relatively simple and quite useful.

Terminology

In the same context you may encounter three different terms that are related but not quite synonyms, and these are:

  • Dependency Injection (DI)
  • Inversion of Control (IoC)
  • Dependency Inversion Principle (DIP)

Dependency Injection (DI)

In software engineering, dependency injection is a design pattern whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

Dependency injection is one form of the broader (more general) technique of inversion of control.

The intent behind dependency injection is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one.

Inversion of Control (IoC)

In software engineering, inversion of control is a design principle used to increase modularity of the program and make it extensible.
More specifically this means that objects do not create other objects on which they rely to do their work. Instead, they get the objects that they need from an outside source. So we invert/revert the control (hence the name IoC), by delegating the responsibility of creating objects/instances to code outside the class where we need these dependencies. Fortunately there are dependency injection frameworks (commonly know as IoC containers) that make our life easier by taking care of creating these objects (the term usually used is getting or  resolving instances). 
There are several basic techniques to implement inversion of control (i.e. factory pattern, service locator pattern, dependency injection and so on).

Dependency Inversion Principle Tutorial (DIP)

In object-oriented design, the dependency inversion principle refers to a specific form of decoupling software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details. The principle states: 
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
In the practical sense this means that you should make use of interfaces when applying this principle as illustrated in the example.

Example of Dependency Injection in ASP.NET MVC 

For this tutorial, let`s consider a simple example where we want to save something to the database and take for instance the user creation in an ASP.NET MVC application. So first we need a UserController class and a Create action method (overlooked return statement for practical reasons).


public class UserController : Controller
    {            
        public ActionResult Create(User user)
        {
            // save user to database            
        }
    }

From here, as usual, we have several options. The worst would be to write here the actual code that saves the user to the database. Adding a separate method in UserController that does the saving would also be bad because:

  • Controllers should have mostly action methods and no business logic
  • It violates the Single Responsibility Principle (aside from its intended responsibility we would charge the controller with the responsibility of saving to the database)

The solution would be to create a separate class that has the responsibility of working with the database.


public class UserController : Controller
    {            
        public ActionResult Create(User user)
        {            
            var repository = new Repository();
            repository.Save(user);
        }
    }

This solves the SRP issue described above but by creating a Repository object in UserController we get a dependency that is hard to change (tight coupling). Suppose we would also want to update existing user information.


public class UserController : Controller
    {            
        public ActionResult Create(User user)
        {            
            var repository = new Repository();
            repository.Save(user);
        }

        public ActionResult Edit(User user)
        {
            var repository = new Repository();
            repository.Update(user);
        }
    }

So now we have two actions with a dependency on Repository but there could also be different entities (i.e. Product, ShoppingCart, Order, etc.) in different controllers, all depending on Repository. Imagine a scenario where we would like to use a different Repository (i.e. MySqlRepository, MongoDbRepository, etc.) or even just provide mock objects for unit testing. We would then have to change every line and replace new Repository() with the one in question. Of course we do not want to do this. First, this is no fun. Second, this is error-prone. Third, this is stupid, repetitive work for a trained monkey. So what do we do? 
This is where Dependency Injection comes in. The solution would be to inject a Repository dependency in the UserController constructor (there is also setter injection and property injection but constructor injection is the most common). By injecting the dependencies we achieve great flexibility in our application since we can now swap, decorate and intercept dependencies without the consuming class knowing. In other words, changing the Repository does not imply any change in UserController (or any other for that matter).


public class UserController : Controller
    {
        private readonly Repository _repository;

        public UserController(Repository repository)
        {
            _repository = repository;
        }

        public ActionResult Create(User user)
        {                                                
            _repository.Save(user);
        }

        public ActionResult Edit(User user)
        {            
            _repository.Update(user);
        }
    }

Now, when we change the Repository, we need only change the constructor parameter and the field. But we can take this even further by following the Dependency Inversion Principle through shared abstractions which will truly imply no change at all in the controllers. Practically speaking we could use an interface that every repository will need to implement. So instead of Repository we use IRepository.


 
public class UserController : Controller
    {
        private readonly IRepository _repository;

        public UserController(IRepository repository)
        {
            _repository = repository;
        }

        public ActionResult Create(User user)
        {                                                
            _repository.Save(user);
        }

        public ActionResult Edit(User user)
        {            
            _repository.Update(user);
        }
    }

As mentioned earlier there are DI frameworks, also know as IoC containers that take care of resolving the instances. For example, using Castle Windsor IoC container we need only register / install the services (in our case the IRepository service). This would look something like this:


 
public void Install(IWindsorContainer container, IConfigurationStore store)
        {
container.Register(Component.For<IRepository>().ImplementedBy<Repository>().LifeStyle.Transient);            
        }

Now if we want to change the Repository we change it only here, in one place. Of course that in order for this to work you also need to add a custom ControllerFactory and install your IoC container registrations but this is beyond the scope of this tutorial which is meant to focus more on the concept and the advantages you get by using Dependency Injection.

Pros
  • loosely coupled code
  • reusable code
  • testable code
  • readable code
  • increases modularity and adds extensibility
Cons
  • adds a degree of complexity
  • the learning curve may be an issue
Conclusion

Even if you never plan to change the injected service (in this case the Repository) and you never plan to do Unit Testing I still highly recommend you to use Dependency Injection whenever is suited and this tutorial is meant to help you succeed easier with your plans (i.e. in controllers as illustrated in the example).

Share on:

Want to stay on top of everything?

Get updates on industry developments and the software solutions we can now create for a smooth digital transformation.

* I read and understood the ASSIST Software website's terms of use and privacy policy.

Frequently Asked Questions

1. What is ASSIST Software's development process?  

The Software Development Life Cycle (SDLC) we employ defines the following stages for a software project. Our SDLC phases include planning, requirement gathering, product design, development, testing, deployment, and maintenance.

2. What software development methodology does ASSIST Software use?  

ASSIST Software primarily leverages Agile principles for flexibility and adaptability. This means we break down projects into smaller, manageable sprints, allowing continuous feedback and iteration throughout the development cycle. We also incorporate elements from other methodologies to increase efficiency as needed. For example, we use Scrum for project roles and collaboration, and Kanban boards to see workflow and manage tasks. As per the Waterfall approach, we emphasize precise planning and documentation during the initial stages.

3. I'm considering a custom application. Should I focus on a desktop, mobile or web app?  

We can offer software consultancy services to determine the type of software you need based on your specific requirements. Please explore what type of app development would suit your custom build product.   

  • A web application runs on a web browser and is accessible from any device with an internet connection. (e.g., online store, social media platform)   
  • Mobile app developers design applications mainly for smartphones and tablets, such as games and productivity tools. However, they can be extended to other devices, such as smartwatches.    
  • Desktop applications are installed directly on a computer (e.g., photo editing software, word processors).   
  • Enterprise software manages complex business functions within an organization (e.g., Customer Relationship Management (CRM), Enterprise Resource Planning (ERP)).

4. My software product is complex. Are you familiar with the Scaled Agile methodology?

We have been in the software engineering industry for 30 years. During this time, we have worked on bespoke software that needed creative thinking, innovation, and customized solutions. 

Scaled Agile refers to frameworks and practices that help large organizations adopt Agile methodologies. Traditional Agile is designed for small, self-organizing teams. Scaled Agile addresses the challenges of implementing Agile across multiple teams working on complex projects.  

SAFe provides a structured approach for aligning teams, coordinating work, and delivering value at scale. It focuses on collaboration, communication, and continuous delivery for optimal custom software development services. 

5. How do I choose the best collaboration model with ASSIST Software?  

We offer flexible models. Think about your project and see which models would be right for you.   

  • Dedicated Team: Ideal for complex, long-term projects requiring high continuity and collaboration.   
  • Team Augmentation: Perfect for short-term projects or existing teams needing additional expertise.   
  • Project-Based Model: Best for well-defined projects with clear deliverables and a fixed budget.   

Contact us to discuss the advantages and disadvantages of each model. 

ASSIST Software Team Members

See the past, present and future of tech through the eyes of an experienced Romanian custom software company. The ASSIST Insider newsletter highlights your path to digital transformation.

* I read and understood the ASSIST Software website's terms of use and privacy policy.

Follow us

© 2025 ASSIST Software. All rights reserved. Designed with love.