La Dépendance Injection (DI) dans C# .NET est un concept clé dans la gestion des dépendances des objets, et elle est souvent utilisée pour améliorer la testabilité, la flexibilité et la maintenabilité du code. Dans .NET Core et .NET 5+, la DI est supportée nativement par le framework, et tu peux l'utiliser pour injecter des services dans des classes via les constructeurs, les propriétés ou les méthodes.
🔹 1. Concept de base de la Dependency Injection (DI)
Le principe de la DI est simple : au lieu de créer des instances d'objets directement dans une classe (ce qui rend la classe dépendante de ces objets), l'instance de l'objet est injectée dans la classe par un container de DI. Cela permet de réduire les dépendances fortes et de rendre le code plus testable et modulaire.
Exemple simple de DI dans C# :
Voici un exemple simple où on injecte une dépendance (un service) dans une classe via le constructeur.
🔹 2. Injection par constructeur
Exemple :
// Interface représentant le service
public interface INotificationService
{
void SendNotification(string message);
}
// Implémentation concrète du service
public class EmailNotificationService : INotificationService
{
public void SendNotification(string message)
{
Console.WriteLine($"Email envoyé : {message}");
}
}
// Classe cliente utilisant le service
public class UserController
{
private readonly INotificationService _notificationService;
// Injection de la dépendance par le constructeur
public UserController(INotificationService notificationService)
{
_notificationService = notificationService;
}
public void RegisterUser(string username)
{
// Logique d'inscription (omise)
_notificationService.SendNotification($"Utilisateur {username} inscrit avec succès.");
}
}
🔹 3. Configurer la DI dans Program.cs
(ou Startup.cs
pour ASP.NET Core)
Dans une application ASP.NET Core, la DI est automatiquement configurée, mais dans d'autres types d'applications (comme une application console), tu dois configurer le container DI manuellement.
Exemple pour une application Console :
using Microsoft.Extensions.DependencyInjection;
class Program
{
static void Main(string[] args)
{
// Configuration du container DI
var serviceProvider = new ServiceCollection()
.AddSingleton<INotificationService, EmailNotificationService>() // Ajouter EmailNotificationService à INotificationService
.AddTransient<UserController>() // Ajouter UserController qui dépend de INotificationService
.BuildServiceProvider();
// Résolution et exécution
var userController = serviceProvider.GetService<UserController>();
userController.RegisterUser("Alice");
}
}
Dans ce cas, EmailNotificationService
est enregistré en tant que singleton (une seule instance pour toute l'application), et UserController
est enregistré en tant que transient (une nouvelle instance à chaque fois qu'on en a besoin).
🔹 4. Types de durée de vie dans la DI
Tu peux spécifier la durée de vie des services que tu enregistres dans le container DI. Il existe trois types principaux :
-
Singleton : Une seule instance du service sera utilisée pendant toute la durée de vie de l'application.
services.AddSingleton<IMyService, MyService>();
-
Transient : Une nouvelle instance du service est créée chaque fois qu'il est demandé.
services.AddTransient<IMyService, MyService>();
-
Scoped : Une instance est créée une seule fois par requête (utile surtout dans des applications web).
services.AddScoped<IMyService, MyService>();
🔹 5. DI dans ASP.NET Core
Dans les applications ASP.NET Core, la DI est généralement configurée dans la méthode ConfigureServices
du fichier Startup.cs
(ou Program.cs
dans .NET 6+).
Exemple de configuration dans ASP.NET Core (Program.cs) :
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// Ajouter les services à DI
services.AddSingleton<INotificationService, EmailNotificationService>();
services.AddScoped<UserController>();
// Ajouter les services requis pour l'application web
services.AddControllersWithViews();
});
}
Ensuite, tu peux utiliser l'injection de dépendance dans tes contrôleurs ASP.NET Core.
Exemple de contrôleur utilisant DI :
public class UserController : Controller
{
private readonly INotificationService _notificationService;
// Injection par constructeur
public UserController(INotificationService notificationService)
{
_notificationService = notificationService;
}
public IActionResult Register(string username)
{
_notificationService.SendNotification($"Utilisateur {username} inscrit.");
return View();
}
}
🔹 6. Testabilité avec DI
L'un des grands avantages de l'utilisation de la DI est qu'elle permet de rendre le code plus testable. Par exemple, tu peux facilement mock ou simuler les dépendances dans tes tests unitaires.
Exemple avec Moq (mocker un service dans un test) :
using Moq;
using Xunit;
public class UserControllerTests
{
[Fact]
public void RegisterUser_ShouldSendNotification()
{
// Arrange
var mockService = new Mock<INotificationService>();
var controller = new UserController(mockService.Object);
// Act
controller.RegisterUser("Alice");
// Assert
mockService.Verify(service => service.SendNotification(It.IsAny<string>()), Times.Once);
}
}
Dans cet exemple, la méthode SendNotification
du service INotificationService
est simulée avec Moq, et on vérifie qu'elle a été appelée une fois lorsque l'on appelle RegisterUser
.
🧠 Conclusion
La Dépendance Injection dans C# .NET est essentielle pour structurer des applications modifiables et testables. Elle permet de séparer la logique d'injection de dépendances de l'implémentation réelle des classes, ce qui rend le code plus flexible et plus facile à maintenir.
-
Injections par constructeur : La méthode la plus courante.
-
Durée de vie des services : Singleton, Scoped et Transient.
-
Testabilité : DI rend les tests unitaires plus simples grâce à la simulation (mocking).