DEV Community

Jaydeep Patil
Jaydeep Patil

Posted on

Dependency Injection and Different ways to inject it using .NET Core API

In this blog, we are going to discuss dependency injection and its usage and benefits. Also, discuss different ways to implement dependency injection.

Prerequisites:

Basic understanding of C# Programming Language.
Understanding of Object-Oriented Programming.
Basic Understanding of .NET Core.

The purpose behind the dependency injection design patterns is:

  • In simple words, dependency means the object depends on another object to do some work.
  • Using Dependency Injection we are able to write loosely coupled classes and because of that our current functionality of one class does not directly depend on another class because of that we can easily maintain, change and unit test code properly.
  • It also takes care of the open-closed principle using abstraction and interface we are able to make some future changes easily without modifying existing functionality.

Dependency Injection:

  • Dependency Injection is used to inject the Object of the class into another class.
  • Dependency Injection uses Inversion of Control to create an object outside the class and use that object using different ways like using Service Container which .NET Core provides.

Now we are looking what is the problem we have faced without using Dependency Injection

Suppose we have a class car, and that depends on the BMW class.

public class Car
{
    private BMW _bmw;
    public Car()
    {
        _bmw = new BMW();
    }
}
public class BMW
{
    public BMW()
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Here in this example class, Car depends on the BMW class, and because they are tightly coupled, we need to create an object of the BMW class each time.

In the future, suppose we want to add some parameters to the BMW Class Constructor, like the model name, as shown in the below example.

public class Car
{
    private BMW _bmw;
    public Car()
    {
        _bmw = new BMW("BMW X1");
    }
}
public class BMW
{
    public BMW(string ModelName)
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

So, in this case, we need to add the Model Name parameter into the Car class, and again, we need to change the constructor and pass the parameter. This is not a good practice in software development, and because of that, there is a hard dependency created, and when our application is large, it’s hard to maintain.

These are the challenges we face while implementing functionality without Dependency Injection.

Uses of Dependency Injection in .NET Core:

.NET Core provides a mechanism like IOC Container that will respond to take care of the following things.

  • The Registration of services with type and class in a container and due to this we are able to inject the services as per our need.
  • The IOC Container also resolves the dependencies of classes of the required class.
  • It also manages the lifetime of the object.

Ways to register Lifetime of Services in startup class:

Scope:

  • It will create a single instance per scope.
  • It will create instances of every request.

Singleton:

  • It will create only a single instance per request and be used throughout the application.
  • It also shared that same instance throughout the application.

Transient:

  • It will make a new instance at each time and not share with other applications.
  • It is used for small and lightweight applications.

Now we are going to create .NET Core Product Application using Dependency Injection one by one.

Image description
Then configure the project

Image description
Provide additional information like .NET Version and other configuration

Image description
Then, install the following NuGet Packages which we need for Swagger, SQL Server, Migration, and Database Update.

Microsoft.EntityFrameworkCore

Microsoft.EntityFrameworkCore.Design

Microsoft.EntityFrameworkCore.SqlServer

Microsoft.EntityFrameworkCore.Tools

Swashbuckle.AspNetCore
Enter fullscreen mode Exit fullscreen mode

Image description
Now, Create the ProductDetail Class inside the Model Folder

using System.ComponentModel.DataAnnotations;
namespace Product.Model
{
    public class ProductDetail
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        public int Cost { get; set; }
        public int NoOfStock { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Later on, Create DBContextClass for the database-related operation of entity framework

using Microsoft.EntityFrameworkCore;
using Product.Model;
namespace Product.Data
{
    public class DBContextClass : DbContext
    {
        public DBContextClass(DbContextOptions<DBContextClass> options) : base(options)
        {
        }
        public DbSet<ProductDetail> Products { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Add the Database Connection string of SQL Server in the appsetting.json file

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=ServerName;Initial Catalog=ProductData;User Id=Test;Password=database@123;"
  }
}
Enter fullscreen mode Exit fullscreen mode

Create the ProductService and IProductService Classes inside Service Folder for data manipulation using Dependency Injection

using Product.Model;
using System.Collections.Generic;
namespace Product.Service
{
    public interface IProductService
    {
        ProductDetail AddProduct(ProductDetail employee);
        List<ProductDetail> GetProducts();
        void UpdateProduct(ProductDetail employee);
        void DeleteProduct(int Id);
        ProductDetail GetProduct(int Id);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, create ProductService Class

using Product.Data;
using Product.Model;
using System.Collections.Generic;
using System.Linq;
namespace Product.Service
{
    public class ProductService : IProductService
    {
        private readonly DBContextClass _context;
public ProductService(DBContextClass context)
        {
            _context = context;
        }
        public ProductDetail AddProduct(ProductDetail product)
        {
            _context.Products.Add(product);
            _context.SaveChanges();
            return product;
        }
public void DeleteProduct(int Id)
        {
            var product = _context.Products.FirstOrDefault(x => x.Id == Id);
            if (product != null)
            {
                _context.Remove(product);
                _context.SaveChanges();
            }
        }
public ProductDetail GetProduct(int Id)
        {
            return _context.Products.FirstOrDefault(x => x.Id == Id);
        }
public List<ProductDetail> GetProducts()
        {
            return _context.Products.OrderBy(a => a.Name).ToList();
        }
public void UpdateProduct(ProductDetail product)
        {
            _context.Products.Update(product);
            _context.SaveChanges();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

After this, Create ProductController and inside that, you can see we inject product service into the constructor easily without being tightly coupled. So it helps to extend the functionality of the Product without modifying existing functionality.

using Microsoft.AspNetCore.Mvc;
using Product.Model;
using Product.Service;
using System.Collections.Generic;
namespace Product.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IProductService productService;
        public ProductController(IProductService _productService)
        {
            productService = _productService;
        }
        [HttpGet]
        [Route("api/Product/GetProducts")]
        public IEnumerable<ProductDetail> GetProducts()
        {
            return productService.GetProducts();
        }
        [HttpPost]
        [Route("api/Product/AddProduct")]
        public IActionResult AddProduct(ProductDetail product)
        {
            productService.AddProduct(product);
            return Ok();
        }
        [HttpPut]
        [Route("api/Product/UpdateProduct")]
        public IActionResult UpdateProduct(ProductDetail product)
        {
            productService.UpdateProduct(product);
            return Ok();
        }
        [HttpDelete]
        [Route("api/Product/DeleteProduct")]
        public IActionResult DeleteProduct(int id)
        {
            var existingProduct = productService.GetProduct(id);
            if (existingProduct != null)
            {
                productService.DeleteProduct(existingProduct.Id);
                return Ok();
            }
            return NotFound($"Product Not Found with ID : {existingProduct.Id}");
        }
        [HttpGet]
        [Route("GetProduct")]
        public ProductDetail GetProduct(int id)
        {
            return productService.GetProduct(id);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, Register the service inside the ConfigureServices method and also add Db Provider and configure the Database connection string which we put in the app settings.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Product.Data;
using Product.Service;
namespace Product
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //DBContext Configuration
            services.AddDbContext<DBContextClass>(options =>
           options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            //Register the ProductService for DI purpose
            services.AddScoped<IProductService, ProductService>();
            //enable swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Product", Version = "v1" });
            });
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Product v1"));
            }
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Our Project structure looks like as shown in the image

Image description
Finally, we are going to create a database and migrations using Entity Framework. So for that open the Package Manager Console in Visual Studio and enter the following command for migration one by one.

Add-Migration "FirstMigration"
database-update

Now, run the Product Application and open the swagger dashboard and use the endpoints.

Image description
There are also following different ways to inject the DI without Controller Constructor

Method 1:

[HttpGet]
[Route("api/Product/GetProducts")]
public IEnumerable<ProductDetail> GetProducts()
{
    //method 1
    var services = this.HttpContext.RequestServices;
    var productService = (IProductService)services.GetService(typeof(IProductService));
    return productService.GetProducts();
}
Enter fullscreen mode Exit fullscreen mode

Method 2:

[HttpPost]
[Route("api/Product/AddProduct")]
public IActionResult AddProduct(ProductDetail product)
{
    //Method 2
    var productService =
   (IProductService)this.HttpContext.RequestServices.GetService(typeof(IProductService));
    productService.AddProduct(product);
    return Ok();
}
Enter fullscreen mode Exit fullscreen mode

Method 3:

//Method 3
public IActionResult UpdateProduct([FromServices] IProductService productService,
ProductDetail product)
{
    productService.UpdateProduct(product);
    return Ok();
}
Enter fullscreen mode Exit fullscreen mode

So, This is all about Dependency Injection. I hope you understand

Happy Coding!

Top comments (0)