Dependency Injection (C# WebApi)

Gabriel Bravo
4 min readJan 15, 2023

--

In this article we will be reviewing what Dependency Injection is and why is usefull in software development.

Depencency Injection is a design pattern that helps us to build loosely coupled, easy readable and maintainable code.

In simple terms, Dependency Injection is a process in which we inject an object into a class that depends on that object.

This techique is the most commonly used design pattern to remove the dependencies between objects.

Let’s see an example without using Dependency Injection

[Route("api/[controller]")]
[ApiController]
public class ExampleController: ControllerBase
{
[HttpGet("input")]
public IActionResult ProcessInput()
{
Service service = new Service();
Mail mail = new Mail();

service.process();
mail.sendNotification();

return Ok();
}

[HttpGet("output")]
public IActionResult ProcessOutput()
{
Service service = new Service();
Mail mail = new Mail();

service.process();
mail.sendNotification();

return Ok();
}
}

In this example we can see two Get methods in a Controller: ProcessInput() and ProcessOutput() and each method implements an instance of Service class and Mail class.

So the behaviour in this example is:

  • httpRequest reaches ProcessInput() / ProcessOutput() methods
  • Service and Mail class are initialized (this means that Controller class does know the implementation of Mail class)
  • process method is executed
  • sendNotification methods are executed and an email is sent to an user account.

Additionally, let’s say that Mail Notification is implemented in several Controllers.

Cons in this implementation

As we can see in this example, Service and Mail classes are directly initialized inside the Get methods in Controller, but, what if:

  • We need to change Constructor of the classes adding some parameters?
  • Need to change the service process?
  • Need to change the notification mechanism?

Let’s consider that we need to change the notificacion mechanism: Now Whatsapp notifications are required.

Wrong Solution:

The wrong solution is spend time changing the Mail class for Whatsapp class

[HttpGet("input")]
public IActionResult ProcessInput()
{
Service service = new Service();
Whatsapp whatsapp = new Whatsapp();

service.process();
whatsapp.sendNotification();

return Ok();
}

This is a bad solution as it takes time to change class dependencies and it does not use any design pattern for notification service.

Implementing Dependency Injection:

The correct way to solve this problem is using Dependency Injection.

The idea of this pattern is that Controller does not use the implementation of Notification service but it uses an abstraction of it instead.

Abstraction is used to hide background details or any unnecessary implementations so the Controller only see the required information.

To do this, We need to create an Interface that will define the method to send the notification and the input/ouput data types (for this case input/ouptut not required).

public interface INotificationService
{
void Send();
}

This interface will be implemented in all Notification services that we require:

public class MailNotificationService: INotificationService
{
public void Send() {
...code for email notification
}
}
public class WhatsappNotificationService: INotificationService
{
public void Send() {
...code for whatsapp notification
}
}

Once we’ve created Notification services and Interface We can use the abstraction (Interface) in the Controller:

[Route("api/[controller]")]
[ApiController]
public class ExampleController: ControllerBase
{
private readonly INotificationService _notificationService
public ExampleController(INotificationService notificationService) {
_notificationService = notificationService
}

[HttpGet("input")]
public IActionResult ProcessInput()
{
Service service = new Service();

service.process();
_notificationService.sendNotification();

return Ok();
}

[HttpGet("output")]
public IActionResult ProcessOutput()
{
Service service = new Service();

service.process();
_notificationService.sendNotification();

return Ok();
}
}

As you can see We only implement the INotificationService and We do not know about the implementation of that (it may be Mail, Whatsapp or SMS).

To use the abstraction we need to:

  • declare a private property of INotificationService type
  • in constructor we pass a property of type INotificationService and asign the value to the private property

and now We only use _notificationService.sendNotification() for our porpusses.

But now, something really important is missing: How the controller receives an instance of INotificationService? and Which notification service will be injected (Mail or Whatsapp)?

To do this We need to modify our Startup.cs file:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

//HERE WE INJECT A SERVICE USING AN ABSTRACTION (INTERFACE)
services.AddScoped<INotificationService, MailNotificationService>();
}

In this example We’re injecting the Mail service to the Controller by using the INotificationService, so when the send() method is invoked the Mail implementation will be executed.

So when we need to implement Whatsapp notification we just need to change the Service injected:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

services.AddScoped<INotificationService, WhatsappNotificationService>();
}

Benefits of Dependency Injection:

  • Reduces coupling between services
  • Increases readable code
  • Increases maintainable code
  • It helps unit testing

In C# WebApi We can use three types of injections:

  • Scoped: Created once per request
  • Singleton: Created the first time they are requested
  • Transient: Created each time the are requested

In this example we use AddScoped that create an instance of the service in each request to the controller.

We’ll be reviewing the different types in another articule.

I hope it helps!

--

--

Gabriel Bravo
Gabriel Bravo

Responses (1)