.NET Core provides built-in support for dependency injection, a technique for achieving Inversion of Control (IoC) between classes and their dependencies. This blog will help you to understand how and why to implement dependency injection in .Net Core.
IServiceProvider is the built-in IoC container included in ASP.NET Core.
IServiceCollection is a collection of service descriptors. This collection registers services with different scope of objects (Transient, Scoped, singleton).
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddScoped<IProduct, ProductService>
Assume you are an owner of GeekShop where you have a website that sells products online. This website calls ASP.NET Web APIs, API has API Controller and APIController has ProductService. ProductService returns product information from Database.
You are having following API code to create a new instance of ProductService for fetching Products from the database.
[HttpGet] [Route("GetProducts")] public List<Product> GetProducts() { ProductService product = new ProductService(); return product.GetProducts(); }
During the holiday season, you want to introduce a big discount sale for 3 days called as Prime Day sale. Now to implement Prime Day sale functionality you created a new PrimeDayProductService however to use this new service you will have to make changes in APIController, also after you are done with the Prime Day discount you will have to update APIController again to serve products as before.
Dependency Injection helps you in this scenario where you completely need to change implementation by Injecting Dependencies to the controller through .NET Core IoC Container.
IoC Container manages the complete object creation, lifetime, and injects required dependencies into classes. It is responsible for
Create an ASP.NET Web API application, add ProductService, PrimeDayServices and inject dependencies to API Controller using .Net Core IoC.
For more information on Create ASP.NET Core Web API with CRUD Operation
Open your Visual Studio -> Click on Create New Project from File Menu -> Select ASP.NET Core Menu -> Add Project name GeeksShop-DI -> Click Next -> select Configuration as shown in following picture.
Create a new folder to the root directory of the application with the name Models. Right-click on the Models folder and add a new Model with the name Product.cs.
Models are helpful for binding data and managing the state of data. For more info on ASP.NET Core Model Binding
Add the following properties to the Product.cs file.
public class Product { public int Id { get; set; } public string Name { get; set; } = string.Empty; public decimal ListPrice { get; set; } }
Add a new folder to the root directory of the application with the name Services.
Right click on Services folder -> Select New Item -> Select Class and name it as ProductService.cs.
Add following code to ProductService.cs file to return list of Products that are available for selling on GeeksShop.
public class ProductService { public List<Product> GetProducts() { List<Product> products =new List<Product>() { new Product() { Id = 1, Name = "Galaxy A13 Mobile", ListPrice = 100 }, new Product() { Id = 2, Name = "Air Pods", ListPrice = 200 }, new Product() { Id = 3, Name = "Pen drive", ListPrice = 300 } }; return products; } }
In this step, you will use Product Service in APIController to serve API clients with available products for selling. If Controllers folder is not available under the root directory then add it.
Right click on Controllers folder -> Select Add -> select Controller -> Click on API from left pane of Add new Scaffolded Item window -> from middle pane select API Controller - Empty -> Give name as ProductController.cs and click Add.
Add the following code to ProductController. It create a instance of ProductService and returns list of Products to API client requests.
[Route("api/[controller]")] [ApiController] public class ProductController : ControllerBase { [HttpGet] [Route("GetProducts")] public List<Product> GetProducts() { ProductService product = new ProductService(); return product.GetProducts(); } }
This type of implementation has a tight coupling between controller and Service. So any change in service will force a change in the controller as well. This tight coupling is happening because the controller is creating an object of ProductService.
To avoid this kind of tight coupling and we should use Interface and inject dependency by IoC container rather than creating objects in Controller.
To achieve the Dependency Inversion Principle We will create IProduct Interface.
Add a new folder with the name Interfaces under the Services folder. This interface will have a method that will be a contract to implement by inherited classes.
Add this code to the IProduct interface.
namespace GeeksShop_DI.Services.Interfaces { public interface IProduct { List<Product> GetProducts(); } }
Update code ProductService to implement IProduct Interface as shown below.
public class ProductService : IProduct { public List<Product> GetProducts() { //return list of products } }
As explained earlier we will have a different service just for serving PrimeDay Products sale. This service will also implement IProduct Interface.
PrimeDay Product service will be used to calculate discounts during PrimeDay festival.
Add a new class file with the name PrimeDayProductService under the Services folder. Add the following code to it, this service returns Products with PrimeDay discounts.
namespace GeeksShop_DI.Services { public class PrimeDayProductService : IProduct { public List<Product> GetProducts() { List<Product> products = new List() { new Product() { Id = 4, Name = "Galaxy A15 Mobile", ListPrice = 400 - PrimeDayDiscount }, new Product() { Id = 5, Name = "Laptop", ListPrice = 500 - PrimeDayDiscount } }; return products; } public int PrimeDayDiscount { get { return 10; } } } }
Previously we created an instance in the controller and called the GetProducts method of ProductService. Now we are going to update ProductController to inject dependency from the constructor of ProductController.
[Route("api/[controller]")] [ApiController] public class ProductController : ControllerBase { IProduct _product; public ProductController(IProduct product) { this._product = product; } [HttpGet] [Route("GetProducts")] public List<Product> GetProducts() { return this._product.GetProducts(); } }
If you run the application now you will get InvalidOperationException: Unable to resolve service for type 'GeeksShop_DI.Services.ProductService' while attempting to activate 'ProductController' because this dependency is not registered with ASP.NET Core Dependency Injection Container.
Program.cs file gives access to Dependency Injection Container. So open Program.cs file and add code as per your requirement of Product Service.
As per your requirement, you will register a service in Program.cs file. So if GeeksShop is serving products you will use service ProductService like this.
builder.Services.AddScoped <IProduct, ProductService>();
Run your Web API, Swagger shows products as normal.
If GeekShop has a PrimeDay sale then you will register PrimeDayProductService as shown below.
builder.Services.AddScoped <IProduct, PrimeDayProductService>();
Run your Web API, Swagger shows products with PrimeDay discounts.