Introduction : qu'est-ce que l'injection de dépendances et pourquoi en a-t-on besoin
\nL'injection de dépendances (DI) est l'un des patrons de conception clés qui implémente le principe d'inversion des dépendances (Dependency Inversion Principle) de SOLID. En pratique, cela signifie que les objets ne créent pas leurs dépendances eux-mêmes, mais les reçoivent de l'extérieur. Cela rend le code flexible, testable et faiblement couplé.
\nDans l'écosystème C# et .NET, la DI est devenue un standard de facto. Depuis .NET Core, la plateforme intègre son propre conteneur d'injection de dépendances, utilisé dans ASP.NET Core, les services d'arrière-plan et même les applications console. Sans comprendre la DI, il est impossible d'imaginer le développement moderne en C#.
\nDans cet article, nous analyserons la DI avec des exemples concrets : de l'injection manuelle à l'utilisation du conteneur intégré .NET. Vous verrez comment vous débarrasser des couplages rigides et rendre le code adapté aux tests unitaires.
\n\nLe problème sans injection de dépendances
\nImaginons un scénario typique : la classe OrderService traite les commandes et envoie des notifications par email. Sans DI, le code ressemble à ceci :
public class EmailSender\n{\n public void Send(string to, string subject, string body)\n {\n Console.WriteLine($"Envoi d'email à {to} : {subject}");\n }\n}\n\npublic class OrderService\n{\n private EmailSender _emailSender;\n\n public OrderService()\n {\n // Couplage rigide — la classe crée elle-même la dépendance\n _emailSender = new EmailSender();\n }\n\n public void ProcessOrder(int orderId)\n {\n // Logique de traitement de la commande...\n _emailSender.Send("user@example.com", "Commande confirmée", $"Votre commande #{orderId} a été acceptée.");\n }\n}\nQuel est le problème ? OrderService est fortement lié à l'implémentation concrète EmailSender. Si nous voulons remplacer l'envoi d'email par des SMS ou de la journalisation, il faudra modifier le code de OrderService. Les tests sont également plus complexes : impossible de substituer le vrai EmailSender par un objet mock.
Solution : injection de dépendances par constructeur
\nLa première étape vers la DI est d'extraire une abstraction (interface) et de passer la dépendance via le constructeur. C'est ce qu'on appelle l'injection par constructeur (Constructor Injection).
\n\nÉtape 1 : Créer l'interface
\npublic interface INotificationService\n{\n void Send(string to, string subject, string body);\n}\n\nÉtape 2 : Implémenter l'interface
\npublic class EmailSender : INotificationService\n{\n public void Send(string to, string subject, string body)\n {\n Console.WriteLine($"Email envoyé : {subject} -> {to}");\n }\n}\n\npublic class SmsSender : INotificationService\n{\n public void Send(string to, string subject, string body)\n {\n Console.WriteLine($"SMS envoyé : {subject} -> {to}");\n }\n}\n\nÉtape 3 : Injecter la dépendance via le constructeur
\npublic class OrderService\n{\n private readonly INotificationService _notificationService;\n\n // La dépendance est transmise de l'extérieur\n public OrderService(INotificationService notificationService)\n {\n _notificationService = notificationService;\n }\n\n public void ProcessOrder(int orderId)\n {\n // Logique de traitement de la commande...\n _notificationService.Send("user@example.com", "Commande confirmée", $"Votre commande #{orderId} a été acceptée.");\n }\n}\nDésormais, OrderService ne sait pas quelle implémentation est utilisée. Nous pouvons lui fournir EmailSender, SmsSender ou toute autre classe implémentant INotificationService. Le code est devenu flexible et testable.
Utilisation du conteneur IoC intégré dans .NET
\nCréer manuellement toutes les dépendances n'est pas pratique. .NET dispose d'un conteneur intégré (conteneur IoC) qui gère automatiquement le cycle de vie des objets. Prenons un exemple pour une application console.
\n\n