Injection par constructeur et interfaces
Appliquez l’injection par constructeur avec une conception fondée sur les interfaces afin de découpler les composants et de permettre les tests unitaires.
Injection par constructeur et interfaces est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
Principes de base de l’injection par constructeur
L’injection par constructeur est le modèle DI le plus courant. Une classe déclare ses dépendances comme paramètres du constructeur, et le conteneur les fournit lors de l’instanciation.
Ainsi, les dépendances sont explicites, obligatoires et visibles.
Définir l’interface
Commencez par une interface qui définit le contrat. Vous pourrez ainsi remplacer différentes implémentations, réelles, simulées ou en mémoire, sans modifier le code qui en dépend.
public interface IProductRepository
{
Task<Product?> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
}Implémenter l’interface
L’implémentation concrète s’occupe du travail réel : appels à la base de données, requêtes HTTP, etc. Le code client n’a jamais besoin de savoir quelle implémentation lui est fournie.
public class SqlProductRepository : IProductRepository
{
private readonly AppDbContext _db;
public SqlProductRepository(AppDbContext db) => _db = db;
public async Task<Product?> GetByIdAsync(int id)
=> await _db.Products.FindAsync(id);
public async Task<IEnumerable<Product>> GetAllAsync()
=> await _db.Products.ToListAsync();
public async Task AddAsync(Product product)
{
_db.Products.Add(product);
await _db.SaveChangesAsync();
}
}Consommer le service par le constructeur
La couche de services reçoit IProductRepository comme paramètre du constructeur. Elle ne connaît jamais SqlProductRepository : elle ne dépend que du contrat défini par l’interface.
public class ProductService
{
private readonly IProductRepository _repo;
private readonly ILogger<ProductService> _logger;
public ProductService(
IProductRepository repo,
ILogger<ProductService> logger)
{
_repo = repo;
_logger = logger;
}
public async Task<Product?> GetProductAsync(int id)
{
_logger.LogInformation("Fetching product {Id}", id);
return await _repo.GetByIdAsync(id);
}
}Enregistrer la chaîne de dépendances
Enregistrez chaque service de la chaîne. Le conteneur résout automatiquement l’ensemble du graphe de dépendances : ProductService → IProductRepository → AppDbContext.
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlite("Data Source=app.db"));
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.AddScoped<ProductService>();Tester avec un objet simulé
Comme les dépendances sont injectées via des interfaces, les tests unitaires peuvent utiliser un objet simulé ou un substitut sans accéder à la base de données. C’est l’un des principaux avantages de DI.
// Using NSubstitute or Moq in a unit test
var mockRepo = Substitute.For<IProductRepository>();
mockRepo.GetByIdAsync(1).Returns(new Product { Id = 1, Name = "Widget" });
var logger = Substitute.For<ILogger<ProductService>>();
var svc = new ProductService(mockRepo, logger);
var result = await svc.GetProductAsync(1);
Assert.Equal("Widget", result?.Name);Plusieurs paramètres de constructeur
Une classe peut avoir de nombreux paramètres de constructeur. Le conteneur les résout tous dès lors que chacun est enregistré. Gardez des constructeurs ciblés : un trop grand nombre de paramètres indique souvent que la classe assume trop de responsabilités.
public class OrderService
{
public OrderService(
IOrderRepository orders,
IProductRepository products,
IEmailSender email,
ILogger<OrderService> logger)
{
// all injected by DI container
}
}Éviter l’anti-modèle du localisateur de services
N’injectez pas (NOT) IServiceProvider et n’appelez pas GetService dans les méthodes métier. Cela masque les dépendances et complique les tests. Privilégiez l’injection par constructeur pour obtenir un code explicite et facilement testable.
// BAD
public void Process(IServiceProvider sp)
{
var repo = sp.GetService<IProductRepository>(); // hidden dep!
}
// GOOD
public class Processor
{
private readonly IProductRepository _repo;
public Processor(IProductRepository repo) => _repo = repo;
}Syntaxe des constructeurs primaires (C# 12)
C# 12 introduit les constructeurs primaires pour tous les types de classes. Vous pouvez ainsi déclarer directement les paramètres dans la déclaration de classe et écrire un code DI plus concis.
// C# 12 primary constructor
public class ProductService(
IProductRepository repo,
ILogger<ProductService> logger)
{
public async Task<Product?> GetAsync(int id)
{
logger.LogInformation("Getting {Id}", id);
return await repo.GetByIdAsync(id);
}
}Remplacer les implémentations
Un avantage essentiel de la DI fondée sur des interfaces est de pouvoir remplacer une implémentation en une seule ligne, sans modifier le code qui l’utilise. C’est utile pour changer de base de données, de fournisseur d’envoi d’e-mails ou d’indicateurs de fonctionnalités.
// Switch from SQL to in-memory for integration tests
if (environment.IsEnvironment("Test"))
builder.Services.AddScoped<IProductRepository, InMemoryProductRepository>();
else
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();Cas concret : contrôleur avec DI
Les contrôleurs ASP.NET Core utilisent nativement l’injection par constructeur. Le framework résout tous les paramètres et les transmet au contrôleur.
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly ProductService _svc;
public ProductsController(ProductService svc) => _svc = svc;
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var product = await _svc.GetProductAsync(id);
return product is null ? NotFound() : Ok(product);
}
}Vérification rapide
Quel est le principal avantage de programmer avec des interfaces lorsqu’on utilise DI ?
Récapitulatif : injection par constructeur et interfaces
Points essentiels :
- Déclarez les dépendances comme paramètres du constructeur : le conteneur les fournit
- Définissez des interfaces pour les contrats de service ; injectez l’interface, et non le type concret
- Les interfaces facilitent l’utilisation d’objets simulés dans les tests unitaires et le remplacement des implémentations
- Les constructeurs primaires de C# 12 réduisent le code répétitif dans les classes qui utilisent beaucoup DI
- Évitez l’anti-modèle du localisateur de services : gardez les dépendances explicites
Questions Fréquemment Posées
La leçon « Injection par constructeur et interfaces » est-elle gratuite ?
Oui — le texte complet de « Injection par constructeur et interfaces » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Injection par constructeur et interfaces » ?
Appliquez l’injection par constructeur avec une conception fondée sur les interfaces afin de découpler les composants et de permettre les tests unitaires. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Injection par constructeur et interfaces » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Fondamentaux des conteneurs DI
- Durées de vie des services : Transient, Scoped, Singleton
- Injection par constructeur et interfaces
- Patrons Factory et Options