[Design Pattern] - Dependency Injection trong ASP.NET Core

Dependency Injection is an extremely common technique for loosening objects and the classes that depend on them. Sounds too confusing, right?

This post describes this technique and gives you an overview (hopefully an objective one) of DI

1. The problem

Imagine you want to drink a coke and there are no bottles left at home. You have to go out to Circle K, wander around the store looking for a bottle of coke, pay, and come home

  • You could forget to pay
  • You could fail to find any Circle K nearby
  • You could fail to find any bottle of coke in the circle k
  • You could grab a bottle of Chương Dương sarsi by mistake instead of coke

With Dependency Injection things go differently

You hand money to a kid whose job is buying soft drinks for the neighborhood. 5 minutes later they show up with your bottle of coke

Someone else in the same neighborhood has almost the same need, but they want to drink pepsi

Instead of also going to the store to buy pepsi themselves and hitting the same problems you hit, they also call that kid over, and 5 minutes later a chilled bottle of pepsi is in their hands. That kid is a Service, and you depend on that kid to have a soft drink

2. The benefits

Back to the example above: what do you gain from using the kid’s soft-drink-buying Service?

  • You don’t have to care what the kid does to get you the drink
  • You can ask for many different kinds of drink without knowing what they look like or where they’re sold

3. Back to code

Back in code, how do you implement DI?

3.1. Interface

public interface IDrinkBuyer { void BuyDrink(string name); }

When the drink-buying kid above grows old, they’ll want to pass the drink-buying job on to another kid, and so on. Every drink-buying kid in the neighborhood has one basic method, BuyDrink, taking one param: the name of the drink to buy. You play the role of the WebApplication and call this method to get a drink

3.2. Implementation

The manager of these drink-buying kids assigns each kid their task

  • Kid A buys at Circle K
  • Kid B buys at Vinmart

And they implement it like this

public class CircleKDrinkBuyer: IDrinkBuyer 
{
  public void BuyDrink(string name) 
  {
    // Go to circle k 
    // Find the -name- drink 
    // Buy it and deliver 
  }
}

public class VinmartDrinkBuyer: IDrinkBuyer 
{
  public void BuyDrink(string name) 
  {
    // Go to Vinmart 
    // Find the -name- drink 
    // Buy it and deliver 
  }
}

That’s it. When your house is near Vinmart, the manager posts kid B - VinmartDrinkBuyer - in front of your gate. Whenever you need to buy a drink, you call them. You have no idea that kid B only knows how to buy drinks at Vinmart. All you care about is that you call them and you get a drink

4. Register

So how do you declare it in your app? When you create a new asp.net core 2 app, there are already a few methods to get you started right away

method in startup.cs

by adding the line services.AddScoped(); you have declared that every class using the BuyDrink method will buy drinks at CircleK

4.1. Lifetime

AddScoped sets this service’s lifetime; there are 3 kinds:

  • Transient: every call to it is a completely new instance. This only fits super lightweight services that hold no state
  • Scoped: within one request it is created exactly once
  • Singleton: it is created the first time it is called and lives on until your app dies

4.2. Contructor Injection

After registering your service with a suitable lifetime, the next thing is injecting it where it’s needed (usually a controller). ASP.NET Core 2 lets you inject a service into a controller through that controller’s constructor

inject to controller

4.3. Action Injection

Sometimes you only need this service in one specific action of the controller, and then the way is a bit different

public IActionResult About([FromServices] IDrinkBuyer drinkBuyer) 
{
  drinkBuyer.BuyDrink("coca");
  return View();
}

4.4. A Service inside a Service

Nested services, child services, and so on and so on

Sometimes you need a child service for the parent service to run — so how do you inject it?

Very simple: constructor injection again

Say the DrinkBuyer Service needs the Trasport service to run

public interface ITransport
{
  void Transport();
}
public class BikeTransport: ITransport
{
  public void Transport() 
  {
    // transport by bike 
  }
}

public class CarTransport: ITransport
{
  public void Transport()
  {
    // transport by car
  }
}

first register it in Startup.cs: services.AddScoped();

then you inject it into DrinkBuyer like this

public class CircleKDrinkBuyer: IDrinkBuyer 
{
  private readonly ITransport _transport;
  
  public CircleKDrinkBuyer(ITransport transport) 
  {
      _transport = transport;
  }
  
  public void BuyDrink(string name) {
    // Go to circle k 
    // Find the -name- drink 
    // Buy it 
    // Deliver 
    _transport.Transport();
  }
}

and that’s it :D