🔹 1. Qu'est-ce que l’injection de dépendances (Dependency Injection) et pourquoi est-elle utilisée ?
✅ Définition :
L’injection de dépendances (DI) est un design pattern qui consiste à fournir les dépendances d’un objet depuis l’extérieur, plutôt que de les créer à l’intérieur.
Autrement dit, au lieu de :
public class OrderService
{
private EmailService _emailService = new EmailService(); // dépendance directe ❌
}
on écrit :
public class OrderService
{
private readonly IEmailService _emailService;
public OrderService(IEmailService emailService) // injection par constructeur ✅
{
_emailService = emailService;
}
}
✅ Objectifs :
-
Découpler les composants (principe SOLID : D = Dependency Inversion).
-
Faciliter les tests unitaires (mocking, substitution).
-
Simplifier la maintenance et l’évolution du code.
-
Favoriser la réutilisation et la configuration centralisée des dépendances.
🧠 En résumé :
La DI permet d’écrire un code modulaire, testable et extensible, en confiant la création des objets à un conteneur IoC.
🔹 2. Quelle est la différence entre Service Locator et Dependency Injection ?
Critère |
Service Locator |
Dependency Injection |
Principe |
L’objet va chercher ses dépendances dans un conteneur global |
Les dépendances sont fournies automatiquement à l’objet |
Couplage |
Couplage fort au conteneur (anti-pattern) |
Couplage faible, respecte l’inversion de dépendances |
Lisibilité |
Masque les dépendances réelles |
Les dépendances sont explicites |
Testabilité |
Difficile à tester |
Facile à mocker et injecter |
Exemple Service Locator (❌ à éviter) :
var emailService = ServiceLocator.GetService<IEmailService>();
Exemple Dependency Injection (✅) :
public class OrderService
{
public OrderService(IEmailService emailService) { ... }
}
🧠 En résumé :
Le Service Locator cache les dépendances → difficile à maintenir.
La Dependency Injection les rend explicites → claire et testable.
🔹 3. Quels sont les trois types d’injection de dépendances ?
Type d’injection |
Description |
Exemple |
Constructeur |
Dépendances fournies via le constructeur (le plus courant) |
public MyClass(IMailService mailService) |
Propriété |
Dépendances assignées après création via des propriétés publiques |
public IMailService MailService { get; set; } |
Méthode |
Dépendances passées en paramètre d’une méthode spécifique |
void Initialize(IMailService mailService) |
Exemple global :
public class NotificationService
{
// 1️⃣ Constructeur
public NotificationService(IEmailService emailService) { ... }
// 2️⃣ Propriété
public ISmsService SmsService { get; set; }
// 3️⃣ Méthode
public void Init(ILogService logService) { ... }
}
🧠 Bonne pratique :
L’injection par constructeur est la plus recommandée car elle garantit que toutes les dépendances sont fournies dès la création de l’objet.
🔹 4. Comment configurer l'injection de dépendances dans ASP.NET Core ?
ASP.NET Core intègre un conteneur IoC natif via IServiceCollection
et IServiceProvider
.
Étapes :
-
Déclarer une interface et son implémentation :
public interface IEmailService
{
void SendEmail(string to, string message);
}
public class EmailService : IEmailService
{
public void SendEmail(string to, string message) => Console.WriteLine($"Email sent to {to}");
}
-
Enregistrer le service dans Program.cs
ou Startup.cs
:
builder.Services.AddScoped<IEmailService, EmailService>();
-
L’injecter dans un contrôleur ou un service :
public class OrderController : Controller
{
private readonly IEmailService _emailService;
public OrderController(IEmailService emailService)
{
_emailService = emailService;
}
public IActionResult Confirm()
{
_emailService.SendEmail("client@mail.com", "Order confirmed!");
return Ok();
}
}
🧠 Résumé :
L’enregistrement se fait dans le IServiceCollection
, et l’injection se fait automatiquement par le framework.
🔹 5. Quelle est la différence entre les portées de services : Transient, Scoped et Singleton ?
Portée |
Durée de vie |
Description |
Exemple |
Transient |
À chaque injection |
Nouvelle instance à chaque appel |
AddTransient<IService, Impl>() |
Scoped |
Par requête HTTP (ASP.NET) |
Même instance pour toute la requête |
AddScoped<IService, Impl>() |
Singleton |
Unique pour toute l’application |
Instance partagée et persistante |
AddSingleton<IService, Impl>() |
Exemple pratique :
builder.Services.AddTransient<IEmailService, EmailService>(); // Nouveau à chaque injection
builder.Services.AddScoped<IUserService, UserService>(); // 1 par requête
builder.Services.AddSingleton<ILogService, LogService>(); // Unique globale
🧠 Bonnes pratiques :
-
Transient → objets légers, stateless.
-
Scoped → services dépendant du contexte de requête (EF DbContext).
-
Singleton → services partagés, thread-safe (cache, logger).
🔹 6. Comment utiliser IServiceCollection pour enregistrer des dépendances ?
IServiceCollection
est l’interface utilisée pour configurer le conteneur IoC dans .NET Core.
Exemple d’enregistrement :
public void ConfigureServices(IServiceCollection services)
{
// Enregistre un service simple
services.AddScoped<IEmailService, EmailService>();
// Enregistre un service avec fabrique personnalisée
services.AddSingleton<IMyService>(provider => new MyService("param"));
// Enregistre une instance spécifique
var settings = new AppSettings { Timeout = 30 };
services.AddSingleton(settings);
}
Résolution manuelle :
var provider = services.BuildServiceProvider();
var emailService = provider.GetRequiredService<IEmailService>();
🧠 Résumé :
IServiceCollection
permet d’enregistrer les dépendances, et IServiceProvider
les résout à l’exécution.
🔹 7. Qu'est-ce que l’inversion de contrôle (IoC) ?
✅ Définition :
L’Inversion de Contrôle (Inversion of Control) est un principe architectural où le contrôle du flux d’exécution est inversé :
ce n’est plus votre code qui crée et gère les dépendances, mais le framework ou conteneur IoC.
Exemple sans IoC :
var emailService = new EmailService();
var orderService = new OrderService(emailService);
Exemple avec IoC :
public class OrderService
{
private readonly IEmailService _emailService;
public OrderService(IEmailService emailService)
{
_emailService = emailService;
}
}
➡️ Le conteneur IoC s’occupe de fournir l’instance EmailService
.
🧠 Résumé :
IoC = principe, DI = son implémentation concrète.
Le conteneur gère le cycle de vie, la création et la destruction des objets.
🔹 8. Exemple complet d’injection de dépendances avec Microsoft.Extensions.DependencyInjection
✅ Étape 1 : Ajouter le package (si projet console)
dotnet add package Microsoft.Extensions.DependencyInjection
✅ Étape 2 : Créer les interfaces et implémentations
public interface IMessageService
{
void Send(string message);
}
public class EmailMessageService : IMessageService
{
public void Send(string message)
{
Console.WriteLine($"Email envoyé : {message}");
}
}
✅ Étape 3 : Configurer le conteneur
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddTransient<IMessageService, EmailMessageService>();
var provider = services.BuildServiceProvider();
✅ Étape 4 : Résoudre la dépendance
var msgService = provider.GetRequiredService<IMessageService>();
msgService.Send("Bonjour DI !");
🧠 Résumé :
Microsoft.Extensions.DependencyInjection
fournit le même moteur d’injection que celui d’ASP.NET Core, utilisable dans toute application .NET (console, WPF, etc.).
📘 Récapitulatif synthétique
Concept |
Description |
DI |
Fournit les dépendances depuis l’extérieur |
Service Locator |
Anti-pattern qui cache les dépendances |
Types d’injection |
Constructeur, Propriété, Méthode |
ASP.NET Core DI |
Configurée via IServiceCollection |
Durée de vie |
Transient, Scoped, Singleton |
IoC |
Principe d’inversion de création et contrôle |
Microsoft.Extensions.DependencyInjection |
Framework officiel pour gérer DI dans .NET |
🔹 1. Qu'est-ce que l’Open-Closed Principle (OCP) et comment l’appliquer en C# ?
✅ Définition :
OCP = un principe SOLID qui dit :
“Les classes doivent être ouvertes à l’extension mais fermées à la modification.”
✅ Application en C# :
🧩 Exemple :
public interface IDiscount
{
decimal Apply(decimal amount);
}
public class SeasonalDiscount : IDiscount
{
public decimal Apply(decimal amount) => amount * 0.9m;
}
public class BlackFridayDiscount : IDiscount
{
public decimal Apply(decimal amount) => amount * 0.7m;
}
public class Order
{
private readonly IDiscount _discount;
public Order(IDiscount discount) => _discount = discount;
public decimal FinalAmount(decimal amount) => _discount.Apply(amount);
}
✅ Pour ajouter un nouveau type de réduction, il suffit de créer une nouvelle classe implémentant IDiscount
, sans toucher à Order
.
🔹 2. Comment implémenter un pattern Factory avec l’injection de dépendances ?
✅ Objectif :
Créer des objets dynamquement tout en utilisant la DI pour injecter leurs dépendances.
🧩 Exemple :
public interface IShape { void Draw(); }
public class Circle : IShape { public void Draw() => Console.WriteLine("Circle"); }
public class Square : IShape { public void Draw() => Console.WriteLine("Square"); }
public class ShapeFactory
{
private readonly IServiceProvider _provider;
public ShapeFactory(IServiceProvider provider) => _provider = provider;
public IShape Create(string shapeType) =>
shapeType switch
{
"Circle" => _provider.GetRequiredService<Circle>(),
"Square" => _provider.GetRequiredService<Square>(),
_ => throw new ArgumentException()
};
}
// Registration
services.AddTransient<Circle>();
services.AddTransient<Square>();
services.AddSingleton<ShapeFactory>();
✅ Avantage : on peut créer des objets avec leurs dépendances résolues automatiquement.
🔹 3. Différence entre Activator.CreateInstance() et new T()
Méthode |
Description |
Usage |
new T() |
Compilation-time, type connu |
Direct, performant |
Activator.CreateInstance<T>() |
Runtime, type dynamique |
Création via reflection, moins performant |
🧩 Exemple :
var obj1 = new MyClass(); // Compile-time
var obj2 = Activator.CreateInstance(typeof(MyClass)); // Runtime
🧠 Résumé :
Activator
= utile pour plugins, assemblies dynamiques, mais plus lent.
🔹 4. Pourquoi et comment utiliser Options Pattern dans ASP.NET Core ?
✅ Objectif :
Gérer la configuration fortement typée (appsettings.json) avec DI.
🧩 Exemple :
public class MySettings { public string ApiKey { get; set; } }
// Registration
services.Configure<MySettings>(Configuration.GetSection("MySettings"));
// Injection
public class MyService
{
private readonly MySettings _settings;
public MyService(IOptions<MySettings> options) => _settings = options.Value;
}
✅ Avantages : centralisation, validation, injectabilité et immutabilité.
🔹 5. Qu’est-ce qu’un Service Provider en C# et comment l’utiliser ?
✅ Définition :
IServiceProvider
= contexte DI capable de résoudre les services enregistrés.
🧩 Exemple :
var provider = services.BuildServiceProvider();
var myService = provider.GetService<MyService>();
-
Méthodes : GetService<T>()
, GetRequiredService<T>()
.
-
Sert à résoudre dynamiquement des dépendances, par ex. dans une factory.
🔹 6. Comment gérer les dépendances circulaires dans une application avec DI ?
✅ Problème :
A → B → A → … (boucle infinie)
✅ Solutions :
-
Refactorer pour supprimer la circularité (meilleure pratique)
-
Injecter via Func<T>
ou Lazy<T>
:
public class A { public A(Func<B> bFactory) { var b = bFactory(); } }
-
Injection de propriété ou méthode plutôt que constructeur.
✅ L’idée : retarder la résolution pour casser le cycle.
🔹 7. Différence entre Func et IServiceProvider.GetService()
Concept |
Func |
IServiceProvider.GetService() |
Résolution |
Retardée / lazy |
Résolution immédiate |
Réutilisation |
Peut être appelée plusieurs fois |
Obtenu une seule fois |
DI integration |
Injectable |
Usage ponctuel ou factory |
🧩 Exemple :
public class Foo
{
private readonly Func<Bar> _barFactory;
public Foo(Func<Bar> barFactory) => _barFactory = barFactory;
}
🔹 8. Pourquoi HttpClient doit-il être réutilisé et non instancié à chaque requête ?
-
HttpClient
crée un socket par instance.
-
Si instancié à chaque requête → épuisement des sockets (SocketException
) + performances médiocres.
-
Solution : singleton ou HttpClientFactory.
services.AddHttpClient<MyService>();
✅ HttpClientFactory gère la longévité des sockets et les politiques de retry/retry-backoff.
🔹 9. Comment enregistrer des services conditionnellement dans IServiceCollection
?
🧩 Exemple :
if (useMock)
services.AddSingleton<IMyService, MockService>();
else
services.AddSingleton<IMyService, RealService>();
services.AddSingleton<IMyService>(provider =>
useMock ? new MockService() : new RealService());
🔹 10. Différences entre MediatR et DI classique
Aspect |
DI classique |
MediatR |
Objectif |
Résolution directe de dépendances |
Découplage et envoi de messages/commandes |
Couplage |
Couplage explicite (service A → B) |
Couplage faible via médiateur |
Cas d’usage |
Appels directs, services |
CQRS, Event-driven architecture |
Avantage |
Simple, direct |
Facilite la testabilité, extensions, pipelines (behaviors) |
🧩 Exemple MediatR :
public class CreateOrderCommand : IRequest<bool> { }
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, bool> { ... }
await mediator.Send(new CreateOrderCommand());
✅ Avantage : réduction du couplage, centralisation des pipelines (logging, validation, transaction).
📘 RÉCAPITULATIF SYNTHÉTIQUE
Concept |
Objectif / Utilité |
Exemple |
OCP |
Étendre sans modifier |
Interface IDiscount |
Factory + DI |
Créer objets dynamiquement |
ShapeFactory + IServiceProvider |
Activator.CreateInstance |
Runtime |
Plugins / DLL externe |
Options Pattern |
Config typée injectée |
IOptions |
ServiceProvider |
Résoudre dynamiquement |
provider.GetService() |
Dépendances circulaires |
Eviter boucle |
Lazy, Func |
Func vs GetService |
Résolution retardée |
Lazy factory |
HttpClient |
Réutilisation |
Singleton / HttpClientFactory |
Enregistrement conditionnel |
Choisir implémentation runtime |
if(useMock) AddSingleton<> |
MediatR |
Découplage et CQRS |
IRequest / IRequestHandler |
🔹 1. Comment fonctionne le système d’injection de dépendances en .NET Core ?
-
.NET Core intègre un conteneur IoC (Inversion of Control) par défaut via l’interface IServiceProvider
.
-
Les services sont enregistrés dans IServiceCollection
lors de la configuration (Program.cs
ou Startup.cs
).
-
Lorsque le framework instancie un composant (Controller, Middleware, Razor Page, etc.), il résout automatiquement les dépendances à partir du conteneur.
-
Exemple de résolution :
public class MyController : ControllerBase
{
private readonly IMyService _service;
public MyController(IMyService service)
{
_service = service; // injecté automatiquement
}
}
🔹 2. Différence entre Transient, Scoped et Singleton
Type |
Description |
Exemple typique |
Durée de vie |
Transient |
Nouvelle instance à chaque injection |
Services légers, stateless |
Chaque requête ou chaque injection |
Scoped |
Une instance par scope (par défaut, par requête HTTP) |
Services dépendant du contexte HTTP |
Par requête HTTP |
Singleton |
Une seule instance pour toute l’application |
Cache, configuration, moteur de calcul |
Application entière |
✅ Le choix de la durée de vie impacte mémoire, performance et sécurité des données partagées.
🔹 3. Enregistrer et utiliser un service personnalisé avec DI
-
Définir le service et son interface :
public interface ITradeService { void ExecuteTrade(); }
public class TradeService : ITradeService
{
public void ExecuteTrade() { /* logique trading */ }
}
-
L’enregistrer dans Program.cs
:
builder.Services.AddScoped<ITradeService, TradeService>();
-
L’injecter dans un Controller :
public class TradeController : ControllerBase
{
private readonly ITradeService _tradeService;
public TradeController(ITradeService tradeService)
{
_tradeService = tradeService;
}
}
🔹 4. Injecter une dépendance dans un Middleware ASP.NET Core
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LoggingMiddleware> _logger;
public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
_logger.LogInformation("Processing request");
await _next(context);
}
}
app.UseMiddleware<LoggingMiddleware>();
🔹 5. Configurer une usine de services (Factory Pattern) avec DI
builder.Services.AddSingleton<ITradeService>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return new TradeService(config["TradeMode"]);
});
🔹 6. Avantages d’utiliser l’injection de dépendances
Avantage |
Explication |
Découplage |
Le code dépend d’une interface plutôt que d’une implémentation concrète |
Testabilité |
Permet le mocking ou stub des services pour les tests unitaires |
Réutilisation |
Services centralisés et configurés une seule fois |
Flexibilité |
Facilité pour changer d’implémentation ou de durée de vie sans modifier le code client |
Maintenance simplifiée |
Moins de code « new » dispersé, logique centralisée dans le conteneur DI |
✅ Dans une application financière ou trading, DI permet de centraliser la configuration du moteur de risque, des services de pricing et de l’accès aux données, améliorant la robustesse et la testabilité.