Design Pattern (5-1), Dependency Injection Implementation​​​​​​

Note: this article is published on 07/09/2024.

These will be a series of articles about Design Patterns. We start from MVC Pattern:

A - Introduction

.NET supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.The previous article introduced the Dependency Inversion Principle, and the Dependency Injection in concept. This article will discuss more about the implementation of Dependency Injection. The content of this article:

  • A - Introduction
  • B - Roles in Dependency Injection
  • C - Advantages and disadvantages for Dependency Injection
  • D - Types of Dependency Injection
  • E - Service Lifetimes Managed by DI Container

B - Roles in Dependency Injection

Dependency injection involves four roles: services, clients, interfaces and injectors.

Services and clients

A service is any class which contains useful functionality. In turn, a client is any class which uses services. The services that a client requires are the client's dependencies.

Any object can be a service or a client; the names relate only to the role the objects play in an injection. The same object may even be both a client (it uses injected services) and a service (it is injected into other objects). Upon injection, the service is made part of the client's state, available for use.

Interfaces

Clients should not know how their dependencies are implemented, only their names and API. A service which retrieves emails, for instance, may use the IMAP or POP3 protocols behind the scenes, but this detail is likely irrelevant to calling code that merely wants an email retrieved. By ignoring implementation details, clients do not need to change when their dependencies do.

Injectors

The injector, sometimes also called an assembler, container, provider or factory, introduces services to the client.

The role of injectors is to construct and connect complex object graphs, where objects may be both clients and services. The injector itself may be many objects working together, but must not be the client, as this would create a circular dependency.

Because dependency injection separates how objects are constructed from how they are used, it often diminishes the importance of the new keyword found in most object-oriented languages. Because the framework handles creating services, the programmer tends to only directly construct value objects which represents entities in the program's domain (such as an Employee object in a business app or an Order object in a shopping app)

C - Advantages and disadvantages for Dependency Injection

The Advantages and disadvantages for Dependency Injection:

Advantages

  • A basic benefit of dependency injection is decreased coupling between classes and their dependencies.
  • By removing a client's knowledge of how its dependencies are implemented, programs become more reusable, testable and maintainable.
  • This also results in increased flexibility: a client may act on anything that supports the intrinsic interface the client expects.
  • More generally, dependency injection reduces boilerplate code, since all dependency creation is handled by a singular component.
  • Finally, dependency injection allows concurrent development. Two developers can independently develop classes that use each other, while only needing to know the interface the classes will communicate through. Plugins are often developed by third-parties that never even talk to developers of the original product.

Disadvantages

Critics of dependency injection argue that it:

  • Creates clients that demand configuration details, which can be onerous when obvious defaults are available.
  • Makes code difficult to trace because it separates behavior from construction.
  • Is typically implemented with reflection or dynamic programming, hindering IDE automation.
  • Typically requires more upfront development effort.
  • Encourages dependence on a framework.

D - Types of Dependency Injection

There are three main ways in which a client can receive injected services:

  • Constructor injection, where dependencies are provided through a client's class constructor.
  • Setter or property injection, where the client exposes a setter method which accepts the dependency.
  • Method or Interface injection, where the dependency's interface provides an injector method that will inject the dependency into any client passed to it.

E - Service Lifetimes Managed by DI Container

In the realm of .NET Core development, managing dependencies efficiently is crucial for building scalable and maintainable applications. The .NET Core framework provides a powerful built-in dependency injection (DI) container to handle the instantiation and resolution of dependencies. Understanding service lifetimes is essential for leveraging the full potential of DI in .NET Core applications. In this article, we'll explore the concept of service lifetimes and provide console example to illustrate their usage. This is the content of this article.

Service lifetimes determine how instances of services are created and managed within the DI container. In .NET Core, there are three main service lifetimes.

  1. Transient: Transient services are instantiated each time they are requested. This means that a new instance is created for every injection or retrieval request. Transient services are ideal for lightweight components that do not maintain state between method calls.
  2. Scoped: Scoped services are created once per request within the scope of a specific operation or component. The same instance is reused throughout the request. Scoped services are commonly used in web applications to manage resources that need to be shared within a single request.
  3. Singleton: Singleton services are instantiated only once per application and shared across all requests and components. Once created, the same instance is reused throughout the application's lifetime. Singleton services are suitable for stateful components that maintain a shared state or hold expensive resources.

References: