Dependency Injection
Tags: webdev, csharp, designpatterns, dotnet
Published: 2023-08-25 04:01:03

##What will happen if there is no dependency injection? Why do we need it really?

Let's understand this with example.

Suppose we are building a simple e-commerce application. We have a `UserService` class responsible for handling user-related operations, and it relies on a `UserRepository` class to interact with the database.

```csharp
public class UserRepository
{
    public User GetUserById(int userId)
    {
        // Database logic to retrieve user by ID
    }
}

public class UserService
{
    private UserRepository _userRepository;

    public UserService()
    {
        _userRepository = new UserRepository(); // Creating a dependency internally
    }

    public User GetUser(int userId)
    {
        return _userRepository.GetUserById(userId);
    }
}
```

In this example, the `UserService` class directly creates an instance of `UserRepository` within its constructor. 

This tight coupling leads to challenges:

**1. Hardcoded Dependency:** The `UserService` is tightly coupled to the specific implementation of `UserRepository`. It's hard to swap out the repository with another implementation.

**2. Limited Testability:** Unit testing the `UserService` becomes difficult since we can't easily provide a mock repository for testing purposes.

**3. Rigidity:** If we want to change the way user data is stored (e.g., switch from a database to an API), we would need to modify the `UserService` class, potentially affecting other parts of the application.

**4. Maintainability:** As the codebase grows, the interdependencies between classes become harder to manage. It becomes challenging to understand how different parts of the system are interconnected.

**5. Code Duplication:** If multiple classes need the same dependencies, we might end up duplicating the code for creating those dependencies. This not only increases the chances of errors but also makes the code harder to maintain.

**6. Reusability:** Without proper separation of concerns, it becomes challenging to reuse components in different parts of our application or in different applications altogether.

**7. Flexibility and Extensibility:** Introducing new features or replacing existing components becomes difficult since making changes in one part of the system might have unintended consequences in other parts due to the tightly coupled nature of the code.

**8. Scalability:** As our application grows, managing dependencies and orchestrating their creation manually becomes a more complex task.

Now let's see, how dependency injection makes our code decoupled and solves above problems.

```csharp
public interface IUserRepository
{
    User GetUserById(int userId);
}

public class UserRepository : IUserRepository
{
    public User GetUserById(int userId)
    {
        // Database logic to retrieve user by ID
    }
}

public class UserService
{
    private IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository; // Injecting a dependency through constructor
    }

    public User GetUser(int userId)
    {
        return _userRepository.GetUserById(userId);
    }
}
```
In this revised example:

The `UserService` class depends on the `IUserRepository` interface rather than the concrete `UserRepository` class. This enables us to easily switch implementations by providing a different implementation of `IUserRepository`.

The `UserService` constructor now takes an instance of `IUserRepository` as a parameter. This is dependency injection. We can provide any implementation of `IUserRepository` (such as a mock for testing) when creating an instance of `UserService`.

Testability is greatly improved since we can inject mock repositories during unit testing, isolating the `UserService` from the actual database operations.

By using dependency injection, we achieve better separation of concerns, maintainability, and flexibility in our code. We can adapt our application to changing requirements without making significant changes to existing classes.

##Now see what is dependency injection in details

Dependency Injection (DI) is a software design pattern used in object-oriented programming and software engineering that promotes loose coupling between components or classes. The main idea behind dependency injection is to separate the creation and management of dependencies from the classes that use them. This pattern enhances modularity, testability, maintainability, and flexibility in our codebase.

In simpler terms, rather than a class creating its own dependencies directly, those dependencies are provided to the class from the outside. This reduces the class's reliance on concrete implementations and allows us to easily replace or modify components without altering the class's code.

There are several types of dependency injection:

**1. Constructor Injection:** Dependencies are provided through a class's constructor. This is the most common form of DI and ensures that required dependencies are available before the class is used.

**2. Property Injection:** Dependencies are set through public properties of the class. This approach is less preferred as it might lead to dependencies not being set correctly.

**3. Method Injection:** Dependencies are passed to the methods of a class when they are called. This is useful for methods that require specific dependencies for particular tasks.

There are several popular dependency injection frameworks and containers available for various programming languages, including .NET. These frameworks make it easier to implement and manage dependency injection in your applications. Here are some of the commonly used frameworks for implementing dependency injection in .NET:
(Now .NET Core have inbuilt DI, so we need not to worry about this much.)

**1. Microsoft.Extensions.DependencyInjection (ASP.NET Core DI):**
The built-in dependency injection container provided by Microsoft for ASP.NET Core applications.

**2. Autofac:**
A powerful and flexible DI container for .NET applications.

**3. Ninject:**
A lightweight and easy-to-use DI framework for .NET.

**4. Unity:**
A mature and feature-rich DI container provided by Microsoft.

There are few more, but above are most commonly used ones.