C# Primary Constructor vs Legacy Constructor for Dependency Injection
2026/02/152 min read
bookmark this
C# Primary Constructor vs Legacy Constructor for Dependency Injection
C# 12 introduced primary constructors for classes, making dependency injection (DI) more concise. This post compares the new pattern with the traditional constructor approach, and explains when to use each.
Primary Constructor Syntax (C# 12+)
public class OrderService(IOrderRepository repo, ILogger<OrderService> logger, IEmailSender emailSender)
{
public void ProcessOrder()
{
logger.LogInformation("Processing order...");
// ...
}
}
Pros:
- Less boilerplate: parameters are available as properties.
- Cleaner for simple services.
- Great for record types and DTOs too.
Cons:
- Only available in C# 12+ (.NET 8+).
- No support for additional constructor logic (validation, field assignment, etc.).
- Not as familiar to all teams yet.
Legacy Constructor DI Pattern
public class OrderService
{
private readonly IOrderRepository _repo;
private readonly ILogger<OrderService> _logger;
private readonly IEmailSender _emailSender;
public OrderService(IOrderRepository repo, ILogger<OrderService> logger, IEmailSender emailSender)
{
_repo = repo;
_logger = logger;
_emailSender = emailSender;
// Additional logic (validation, etc.)
}
public void ProcessOrder()
{
_logger.LogInformation("Processing order...");
// ...
}
}
Pros:
- Works in all C# versions.
- Allows custom logic in the constructor.
- Explicit field naming and assignment.
Cons:
- More boilerplate code.
- Slightly more verbose for simple cases.
When to Use Each
| Use Primary Constructor | Use Legacy Constructor |
|---|---|
| Simple services | Need extra constructor logic |
| No custom validation | Need compatibility with older C# |
| C# 12+ codebase | Prefer explicit field naming |
Tip: For most enterprise projects, the legacy pattern is still more common, but primary constructors are great for new, simple services in modern codebases.
References: