Thursday, 7 November 2024

Dependency Injection in Dotnet Core

 Dependency Injection (DI) in .NET Core is a fundamental design pattern that allows objects or services to be passed (injected) into a class rather than the class creating those dependencies itself. This promotes loose coupling and enhances testability and maintainability. .NET Core has built-in support for Dependency Injection via the Microsoft.Extensions.DependencyInjection library.

Key Concepts of Dependency Injection

  1. Service: A service is any class or object that provides some functionality to the application (e.g., database access, logging, or business logic).

  2. Dependency Injection Container: A DI container is responsible for managing the lifecycle of objects and their dependencies. It provides the necessary services and manages how objects are instantiated and injected.

  3. Injection Types:

    • Constructor Injection: Dependencies are provided via the constructor.
    • Property Injection: Dependencies are provided via properties (less common in .NET Core).
    • Method Injection: Dependencies are passed through method parameters.
  4. Lifetime of Services:

    • Transient: A new instance of the service is created each time it is requested.
    • Scoped: A single instance of the service is created per HTTP request, meaning it is shared within the scope of a request.
    • Singleton: A single instance of the service is created and shared throughout the application's lifetime.

How Dependency Injection Works in .NET Core

Step 1: Define Services/Interfaces

Create the interfaces and classes that define the services you want to inject.

public interface IWeatherService
{
    string GetWeather();
}

public class WeatherService : IWeatherService
{
    public string GetWeather()
    {
        return "Sunny";
    }
}

Step 2: Register Services with the DI Container

In .NET Core, services are registered in the ConfigureServices method of Startup.cs or Program.cs. This method adds the services to the DI container, specifying their lifetime. Learn here also

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Register services with different lifetimes
        services.AddTransient<IWeatherService, WeatherService>(); // Transient
        // You can also use services.AddScoped<IWeatherService, WeatherService>();
        // Or services.AddSingleton<IWeatherService, WeatherService>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // Normal middleware pipeline setup
    }
}
  • AddTransient<TService, TImplementation>: Registers a service with a transient lifetime, meaning a new instance is created each time the service is requested.
  • AddScoped<TService, TImplementation>: Registers a service with a scoped lifetime, meaning the service is created once per HTTP request.
  • AddSingleton<TService, TImplementation>: Registers a service with a singleton lifetime, meaning a single instance is used throughout the application's lifetime.

Step 3: Inject Dependencies into Controllers or Services

In ASP.NET Core, dependencies are injected via constructor injection. The framework automatically resolves dependencies by looking at the constructor parameters and providing the appropriate instances from the DI container.

public class WeatherController : ControllerBase
{
    private readonly IWeatherService _weatherService;

    // Constructor Injection
    public WeatherController(IWeatherService weatherService)
    {
        _weatherService = weatherService;
    }

    public IActionResult GetWeather()
    {
        var weather = _weatherService.GetWeather();
        return Ok(weather);
    }
}

In this example, the IWeatherService dependency is injected into the WeatherController. ASP.NET Core automatically provides an instance of WeatherService because it was registered in the DI container.

Step 4: Using DI in Other Classes

You can inject services into any class that the DI container manages, not just controllers. For instance, you might inject a service into a background task or a custom service class.

public class MyService
{
    private readonly IWeatherService _weatherService;

    public MyService(IWeatherService weatherService)
    {
        _weatherService = weatherService;
    }

    public void PerformTask()
    {
        var weather = _weatherService.GetWeather();
        // Do something with the weather data
    }
}

Step 5: Resolving Dependencies at Runtime

When you request a service via DI, you can resolve it in two ways:

  1. Constructor Injection: As shown in the examples above, the DI container will automatically provide dependencies when the class is instantiated.
  2. Manually Resolving a Service: You can manually resolve a service from the DI container using the IServiceProvider interface. This is common in scenarios like background services or factory patterns.
public class MyService
{
    private readonly IServiceProvider _serviceProvider;

    public MyService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void ExecuteTask()
    {
        var weatherService = _serviceProvider.GetRequiredService<IWeatherService>();
        var weather = weatherService.GetWeather();
    }
}

Benefits of Dependency Injection

  • Loose Coupling: Classes don’t manage their own dependencies, making it easier to swap out services.
  • Testability: You can inject mock services for unit testing without needing to change the class.
  • Flexibility: You can configure different lifetimes (transient, scoped, singleton) to manage resource usage and object creation.
  • Separation of Concerns: Classes focus on their primary responsibility, delegating the management of dependencies to the DI container.

Common Issues and Best Practices

  • Circular Dependencies: Be cautious of circular dependencies (A depends on B, and B depends on A). These can cause problems and are generally considered poor design.
  • Overusing DI: Don't overuse DI in situations where it’s not necessary. Sometimes, having a clear, explicit dependency in your class constructor is fine without the need for DI.
  • Single Responsibility: Ensure that services have a single responsibility, and avoid placing too many responsibilities within a single service class. This helps in maintaining code that is easier to understand and test.

Conclusion

In .NET Core, Dependency Injection is a powerful pattern that promotes loose coupling, testability, and maintainability. The framework provides built-in DI support that allows you to register, resolve, and manage dependencies in a very straightforward way. By leveraging DI properly, you can write more modular, maintainable, and scalable applications.


0 comments:

Post a Comment

Topics

ADFS (1) ADO .Net (1) Ajax (1) Angular (47) Angular Js (15) ASP .Net (14) Authentication (4) Azure (3) Breeze.js (1) C# (49) CD (1) CI (2) CloudComputing (2) Coding (8) CQRS (1) CSS (2) Design_Pattern (7) DevOps (4) DI (3) Dotnet (10) DotnetCore (19) Entity Framework (4) ExpressJS (4) Html (4) IIS (1) Javascript (17) Jquery (8) Lamda (3) Linq (10) microservice (4) Mongodb (1) MVC (46) NodeJS (8) React (10) SDLC (1) Sql Server (32) SSIS (3) SSO (1) TypeScript (3) UI (1) UnitTest (2) WCF (14) Web Api (16) Web Service (1) XMl (1)

Dotnet Guru Archives