Constructor injection en interfaces
Pas constructor injection toe met interfacegebaseerd ontwerp om componenten los te koppelen en unit testing mogelijk te maken.
Constructor injection en interfaces is een gratis C# Academy-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject C# Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus C# Academy bevat in totaal 4 lessen.
Basisprincipes van constructorinjectie
Constructorinjectie is het meest voorkomende DI-patroon. Een klasse declareert haar afhankelijkheden als constructorparameters en de container levert deze bij het maken van de instantie.
Hierdoor zijn afhankelijkheden expliciet, verplicht en zichtbaar.
De interface definiëren
Begin met een interface die het contract definieert. Hierdoor kunnen verschillende implementaties (echt, mock, in geheugen) worden verwisseld zonder de afhankelijke code te wijzigen.
public interface IProductRepository
{
Task<Product?> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
}De interface implementeren
De concrete implementatie voert het echte werk uit — databaseaanroepen, HTTP-verzoeken enzovoort. De afnemer hoeft nooit te weten welke implementatie deze krijgt.
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();
}
}Via de constructor gebruiken
De servicelaag neemt IProductRepository als constructorparameter aan. Deze weet niets van SqlProductRepository — alleen van het interfacecontract.
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);
}
}De keten registreren
Registreer elke service in de keten. De container lost de volledige afhankelijkheidsgrafiek automatisch op — ProductService → IProductRepository → AppDbContext.
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlite("Data Source=app.db"));
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.AddScoped<ProductService>();Testen met een mock
Omdat afhankelijkheden via interfaces worden geïnjecteerd, kunnen unit-tests een mock of nepobject doorgeven zonder de database aan te raken. Dit is een van de grootste voordelen van 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);Meerdere constructorparameters
Een klasse kan veel constructorparameters hebben. De container lost ze allemaal op zolang elke parameter is geregistreerd. Houd constructors gericht — te veel parameters zijn een aanwijzing dat de klasse te veel verantwoordelijkheden heeft.
public class OrderService
{
public OrderService(
IOrderRepository orders,
IProductRepository products,
IEmailSender email,
ILogger<OrderService> logger)
{
// all injected by DI container
}
}Het antipatroon service locator vermijden
Injecteer NIET IServiceProvider en roep GetService aan binnen methoden voor bedrijfslogica. Hierdoor worden afhankelijkheden verborgen en wordt testen moeilijk. Geef de voorkeur aan constructorinjectie voor expliciete, goed testbare code.
// 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;
}Syntaxis van primaire constructors (C# 12)
C# 12 introduceert primaire constructors voor alle klassetypen, waarmee je parameters rechtstreeks in de klassedeclaratie kunt opnemen voor beknopte DI-code.
// 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);
}
}Implementaties verwisselen
Een belangrijk voordeel van DI op basis van interfaces: je kunt een implementatie in één regel verwisselen zonder aanroepende code te wijzigen. Handig voor het wisselen van databases, mailproviders of functievlaggen.
// Switch from SQL to in-memory for integration tests
if (environment.IsEnvironment("Test"))
builder.Services.AddScoped<IProductRepository, InMemoryProductRepository>();
else
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();Praktijkvoorbeeld: controller met DI
ASP.NET Core-controllers gebruiken standaard constructorinjectie. Het framework lost alle parameters op en geeft ze door.
[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);
}
}Korte controle
Wat is het belangrijkste voordeel van programmeren tegen interfaces bij het gebruik van DI?
Samenvatting: constructorinjectie en interfaces
Belangrijkste punten:
- Declareer afhankelijkheden als constructorparameters — de container levert ze
- Definieer interfaces voor servicecontracten; injecteer de interface, niet het concrete type
- Interfaces maken eenvoudig mocken in unit-tests en het verwisselen van implementaties mogelijk
- Primaire constructors in C# 12 verminderen herhalende code in klassen met veel DI
- Vermijd het antipatroon service locator — houd afhankelijkheden expliciet
Leer C# met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 93
- Lessen
- 346
Veelgestelde vragen
Is de les “Constructor injection en interfaces” gratis?
Ja — de volledige tekst van “Constructor injection en interfaces” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus C# Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus C# Academy bevat in totaal 4 lessen.
Wat leer ik in “Constructor injection en interfaces”?
Pas constructor injection toe met interfacegebaseerd ontwerp om componenten los te koppelen en unit testing mogelijk te maken. Je oefent met C# Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met C# Academy te beginnen?
Ervaring vooraf is niet nodig. C# Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.
Hoe lang duurt de les “Constructor injection en interfaces”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over C# Academy?
Ja. Elke les over C# Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Basisprincipes van DI-containers
- Levensduur van services: Transient, Scoped, Singleton
- Constructor injection en interfaces
- Factory- en Options-patronen