9-5 Injection de dépendances
Le concept de faire appel à la couche de données fonctionne, mais ce n'est pas encore tellement élégant. En effet, autant pour la fonction Show()
du contrôleur que la fonction Index()
, on utilise la ligne suivante:
EvenementRepository repo = new EvenementRepository(_configuration);
Allons-nous manuellement créer un repository pour chaque action? On pourrait évidemment le créer dans le contrôleur et l'assigner à une variable privée de la classe.
Cependant, nous allons faire une pierre deux coups et:
- Ajouter le
repository
comme variable privée au niveau de la classe - Gérer l'instantication de ce dernier en utilisant le mécanisme d'injection de dépendances de .NET.
Injecter la dépendance à EvenementRepository
Pour rendre la classe EvenementRepository
disponible pour l'injection de dépendance, on utilise une des trois fonctions suivantes sur l'objet Services
(IServiceCollection
).
- AddTransient
- AddScoped
- AddSingleton
Par exemple, pour EvenementRepository
, ajoutez la ligne suivante:
// Injection de dépendances
builder.Services.AddScoped<EvenementRepository>();
Il existe trois fonctions permettant d'ajouter une classe au mécanisme d'injection de dépendance dans .NET.
AddTransient
: une nouvelle instance de la classe est créée dès qu'on demande l'objet.AddScoped
: une seule instance de l'objet est partagée pour la durée de la requête HTTP.AddSingleton
: une seule instance de l'objet est créée pour toute la durée de l'application, l'instance est donc commune à toutes les requêtes et tous les utilisateurs.
Dans le cas du repository, on utilisera un objet propre à la requête.
Finalement, toute cela pour nous permettre de changer le contrôleur et profiter de l'injection de dépendance!
Vous pouvez modifier la classe EvenementsController
pour profiter de l'injection de dépendance comme suit:
public class EvenementsController : Controller
{
private EvenementRepository _evenementRepository;
public EvenementsController(EvenementRepository evenementRepository)
{
_evenementRepository = evenementRepository;
}
[Route("/")]
public async Task<IActionResult> Index()
{
List<Evenement> evenements = await _evenementRepository.GetAll();
return View(evenements);
}
[HttpGet("{id:int}")]
public async Task<IActionResult> Show(int id, string? option)
{
var evenement = await _evenementRepository.FindById(id);
if (evenement == null) return NotFound();
return View(evenement);
}
[HttpGet("{id:int}/edit")]
public IActionResult Edit(int id)
{
return Content("Vue Edit de EvenementsController, id: " + id);
}
}
Utilisation d'une interface (contrat) plutôt qu'une classe
Vous avez surement remarqué l'utilisation abondante des références aux interfaces dans .NET Core MVC.
Par exemple:
- IActionResult
- IConfiguration
- IServiceCollection
- ...
Une interface est en quelque sorte un contrat. Les classes qui implémentent l'interface doivent s'y conformer.
Par exemple, pour le type de retour IActionResult
, nous pourrions créer un nouveau type de réponse ErreurBizarre
, et tant et aussi longtemps que ce type de réponse implémente IActionResult
, on pourra l'utiliser.
C'est donc très flexible. De plus, on sépare ainsi l'implémentation de l'architecture.
Dans le cas de l'injection de dépendances, utiliser une interface plutôt que l'implémentation de la classe permet de dire: "j'ai besoin d'un objet qui a les fonctions suivantes, peu importe l'objet et son type final".
C'est ainsi beaucoup plus flexible.
Vous l'aurez compris, nous allons définir une interface IEvenementRepository
et utiliser cette dernière pour l'injection de dépendances.
Créer l'interface IEvenementRepository
Pour rapidement créer une interface à partir d'une classe existante, il existe un raccourci dans Rider
:
Clic droit
sur le nom de la classeEvenementRepository
- Choisir
Refactor
->Extract Interface...
- Dans la fenêtre, vous assurer d'avoir comme sélection
Its own file
et de cochertous les membres
- Cliquer sur
Next
Modifier Program.cs
On doit dire au mécanisme d'injection de dépendances que lorsqu'une interface IEvenementRepository
est demandée, nous allons retourner une instance de EvenementRepository
.
// Injection de dépendances
builder.Services.AddScoped<IEvenementRepository, EvenementRepository>();
Modifier EvenementsController
Finalement, dans le constructeur de EvenementsController
, c'est à IEvenementRepository
que nous allons faire référence.
public class EvenementsController : Controller
{
private IEvenementRepository _evenementRepository;
public EvenementsController(IEvenementRepository evenementRepository)
{
_evenementRepository = evenementRepository;
}
Vous pouvez vérifier que le tout fonctionne toujours en compilant et en exécutant l'application.