---
sidebar_position: 2
title: DI & IOC
---

## Définition

Lorsque l’on a compris les fondamentaux de la programmation orientée objet (POO), il faut rapidement comprendre et assimiler plusieurs patrons de conceptions.

En effet, cela permet de faire face à des problèmes connus et ainsi d’imposer de bonnes pratiques qui s’appuient sur l’expérience de concepteurs de logiciels.

Pour moi, et sûrement pour beaucoup d’autres concepteurs de logiciels, l’un des premiers objectifs de la conception est de garder une interface claire, facilement maintenable et surtout évolutive. Car tout développeur le sait, les technologies évoluent très très vite.

C’est pourquoi, nous allons voir l’injection de dépendances (DI) et l’inversion de contrôle (Ioc).

### DI (Dependency Injection - injection de dépendances)

En C# il est simple d’instancier un nouvel objet via l’opérateur «new ». L’instanciation d’un nouvel objet dans une classe impose un couplage (c’est-à-dire une connexion étroitement liée), voir même une référence à une assembly.

On pourrait s’en sortir si le projet n’était pas trop complexe avec des objets simples et très peu de couplage. Mais, imaginons que vous vouliez créer un service, comment allez-vous faire pour l’implémenter rapidement dans une nouvelle classe/assembly ? Et dans le pire des cas, le jour où vous voulez changer de type de service dans votre projet simple, qui au fil du temps est devenu complexe, comment allez-vous procéder ?

Vous l’avez compris, ce patron est pour tout développeur qui se soucie de la qualité de son logiciel et cherche à rendre son application la plus maintenable possible.

En effet, l’injection permet d’établir de façon dynamique la relation entre plusieurs classes. Elle consiste à découper les responsabilités entres les différents modules, les différentes parties et facilite même la modification ultérieure des classes.

### IOC (Inversion Of Control - inversion de contrôle)

Une fois que l’on a assimilé le principe de l’injection de dépendances, nous pouvons nous attaquer à un autre concept de plus haut niveau : l’inversion de contrôle.

En effet, si l’on a peu ou beaucoup de classes faisant appel à notre service, nous créerons autant de fois que nécessaire des instances `IMyService` en amont. 
Nous nous retrouverons donc avec plusieurs instances, ce qui ne devait pas se produire. Aussi, le fait d’instancier notre interface à plusieurs endroits, nous nous retrouverons face à autant de duplication de code que d’instanciations, ce qui rendra notre application difficilement maintenable.

Par conséquent, la première idée qui nous vient à l’esprit lorsque l’on maîtrise mal l’inversion de dépendances est de rendre notre classe `MyService` statique. 
Ce qui est une très mauvaise idée. On n’a plus de problème d’instances par contre, à présent, il nous faudra rendre notre classe `thread-safe` et bien gérer le `multi-threading`.
Sans parler des problèmes auxquels nous seront confrontés lorsqu’on voudra faire des tests unitaires.

D’autres aurons pensé au `singleton`. Il n’en est rien, il y a bien souvent trop de problèmes de fuite de mémoire.

Ensuite, lorsque l’on cherche dans ses fondamentaux, on pense tout naturellement à un des patrons de conception décrit par le « Gof » : la `Factory`.

En effet, une factory avec un singleton pourrait être la solution, malheureusement, il resterait toujours un couplage faible entre la classe et l’appel à chaque factory. 

Dans notre exemple, nous n’en aurions qu’une, on n’en voit donc pas l’intérêt. 
Mais, dans une application il y a largement plus de factory et il y aurait donc autant de couplages que de factory. Sans compter sur les instanciations qui peuvent être plus ou moins longues. 
Or dans certaines conditions nous pourrions en voir immédiatement sans devoir attendre le temps d’instanciation.

Pour pouvoir encore plus externaliser du code, comme la création de notre objet, et ne pas ralentir notre programme s’il y a des instanciations longues, nous allons devoir mapper les contrats avec leurs implémentations au démarrage de l’application.

L’IOC ce défini comme un conteneur qui détermine ce qui doit être instancié et retourné au client pour éviter que ce dernier appel explicitement le constructeur avec l’opérateur « new ».

En résumé, c’est un objet qui agit comme un cache pour les instances dont nous avons besoin dans diverses parties de nos applications.

## Ajouter des services à une application Blazor Server

Après avoir créé une application, examinez une partie du fichier `Program.cs` :

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
```

La variable `builder` représente un objet `Microsoft.AspNetCore.Builder.WebApplicationBuilder` avec une propriété `IServiceCollection`, qui est une liste d’objets de descripteur de service. 
Les services sont ajoutés en fournissant des descripteurs de service à la collection de services. 

L’exemple suivant illustre le concept avec l’interface `IDataAccess` et son implémentationconcrète `DataAccess` :

```csharp
builder.Services.AddSingleton<IDataAccess, DataAccess>();
```

## Durée de vie du service

Les services peuvent être configurés avec les durées de vie suivantes :

### `Scoped`

Les applications Blazor WebAssembly n’ont actuellement pas de concept d’étendues de DI. Les services `Scoped` inscrits se comportent comme des services `Singleton`.

Le modèle d’hébergement Blazor Server prend en charge la durée de vie `Scoped` des requêtes HTTP, mais pas entre `SignalR` et les messages de connexion/circuit parmi les composants chargés sur le client. 

La partie Pages Razor ou MVC de l’application traite normalement les services délimités et recrée les services sur chaque requête HTTP lors de la navigation entre des pages ou des vues ou d’une page ou d’un composant. 
Les services délimités ne sont pas reconstruits lors de la navigation entre les composants sur le client, où la communication vers le serveur se déroule sur la connexion `SignalR` du circuit de l’utilisateur, et non via les requêtes HTTP. 

Dans les scénarios de composant suivants sur le client, les services délimités sont reconstruits, car un nouveau circuit est créé pour l’utilisateur :
* L’utilisateur ferme la fenêtre du navigateur. L’utilisateur ouvre une nouvelle fenêtre et retourne à l’application.
* L’utilisateur ferme un onglet de l’application dans une fenêtre de navigateur. L’utilisateur ouvre un nouvel onglet et retourne à l’application.
* L’utilisateur sélectionne le bouton recharger/actualiser du navigateur.

### `Singleton`

La DI crée une instance unique du service. Tous les composants nécessitant un service `Singleton` reçoivent la même instance du service.

### `Transient`

Chaque fois qu’un composant obtient une instance d’un service `Transient` à partir du conteneur de service, il reçoit une nouvelle instance du service.

## Demander un service dans un composant

Une fois que les services sont ajoutés à la collection de services, injectez les services dans les composants à l’aide de la directive Razor `@inject`, qui a deux paramètres :

* Type : type du service à injecter.
* Propriété : nom de la propriété qui reçoit le service d’application injecté. La propriété ne nécessite pas de création manuelle. Le compilateur crée la propriété.

Utilisez plusieurs instructions `@inject` pour injecter différents services.

L'exemple suivant montre comment utiliser `@inject`. 

L’implémentation du service `Services.IDataAccess` est injectée dans la propriété `DataRepository` du composant. Notez comment le code utilise uniquement l’abstraction `IDataAccess` :

```csharp
@page "/customer-list"
@inject IDataAccess DataRepository

@if (customers != null)
{
    <ul>
        @foreach (var customer in customers)
        {
            <li>@customer.FirstName @customer.LastName</li>
        }
    </ul>
}

@code {
    private IReadOnlyList<Customer>? customers;

    protected override async Task OnInitializedAsync()
    {
        customers = await DataRepository.GetAllCustomersAsync();
    }

    private class Customer
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
    }

    private interface IDataAccess
    {
        public Task<IReadOnlyList<Customer>> GetAllCustomersAsync();
    }
}
```

En interne, la propriété générée (`DataRepository`) utilise l’attribut `[Inject]`. 
En règle générale, cet attribut n’est pas utilisé directement. 
Si une classe de base est requise pour les composants et les propriétés injectées sont également requises pour la classe de base, ajoutez manuellement l’attribut `[Inject]` :

```csharp
using Microsoft.AspNetCore.Components;

public class ComponentBase : IComponent
{
    [Inject]
    protected IDataAccess DataRepository { get; set; }

    ...
}
```

## Utiliser l’injection de dépendances dans les services

Les services complexes peuvent nécessiter des services supplémentaires. 

Dans l’exemple suivant, `DataAccess` nécessite le service `HttpClient` par défaut. `@inject` (ou l’attribut `[Inject]`) n’est pas disponible pour une utilisation dans les services. 
L’injection de constructeur doit être utilisée à la place. 
Les services requis sont ajoutés en ajoutant des paramètres au constructeur du service. 
Lorsque l’authentification unique crée le service, elle reconnaît les services dont il a besoin dans le constructeur et les fournit en conséquence. 

Dans l’exemple suivant, le constructeur reçoit un `HttpClient` via DI. `HttpClient` est un service par défaut.

```csharp
using System.Net.Http;

public class DataAccess : IDataAccess
{
    public DataAccess(HttpClient http)
    {
        ...
    }
}
```

Conditions préalables pour l’injection de constructeur :

* Un constructeur doit exister dont les arguments peuvent tous être remplis par DI. Les paramètres supplémentaires non couverts par l’injection de dépendances sont autorisés s’ils spécifient des valeurs par défaut.
* Le constructeur applicable doit être `public`.
* Un constructeur applicable doit exister. En cas d’ambiguïté, DI lève une exception.

