Dependency Injection (DI) เป็น Design Pattern ที่แยกการสร้าง Dependency (Object ที่ต้องการ) ออกจาก Class ที่ใช้ Dependency เพื่อลด Coupling และเพิ่ม Testability/Maintainability
4 ประเภทหลักของ DI
| ประเภท | วิธีส่ง Dependency | เหมาะสำหรับ |
|---|---|---|
| Constructor Injection | ผ่าน Constructor Parameter | Mandatory Dependencies (ส่วนใหญ่) |
| Setter/Property Injection | ผ่าน Public Property (Set) | Optional Dependencies |
| Interface Injection | ผ่าน Interface Setter Method | Legacy/Plugin Systems |
| Service Locator | ขอจาก Container เอง (Anti-pattern) | หลีกเลี่ยง (Hidden Dependencies) |
1. Constructor Injection (แนะนำมากที่สุด)
public class OrderService
{
private readonly IEmailService _email;
private readonly ILogger _logger;
// Dependencies ถูก Require ตอนสร้าง Object
public OrderService(IEmailService email, ILogger logger)
{
_email = email ?? throw new ArgumentNullException(nameof(email));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void PlaceOrder(Order order)
{
_logger.Log("Placing order...");
// ...
_email.SendConfirmation(order.CustomerEmail);
}
}
2. Setter/Property Injection
public class ReportGenerator
{
// Optional Dependency
public IExportService ExportService { get; set; }
public void Generate()
{
// ใช้ได้ถ้ามี Set ไว้
ExportService?.Export(data);
}
}
// Usage
var generator = new ReportGenerator();
generator.ExportService = new PdfExportService(); // Optional
3. Interface Injection
public interface ILoggerInjector
{
void InjectLogger(ILogger logger);
}
public class PaymentProcessor : ILoggerInjector
{
private ILogger _logger;
public void InjectLogger(ILogger logger) => _logger = logger;
public void Process() => _logger?.Log("Processing...");
}
4. Service Locator (Anti-Pattern - หลีกเลี่ยง)
// ❌ ไม่แนะนำ - Hidden Dependencies
public class BadExample
{
public void DoWork()
{
var logger = ServiceLocator.Current.GetInstance<ILogger>(); // Hidden!
logger.Log("Working...");
}
}
ข้อดีของ DI
- Loose Coupling: Class ไม่รู้จัก Concrete Implementation
- Testability: Mock Dependencies ได้ง่ายใน Unit Test
- Reusability: ใช้ Class ซ้ำได้กับ Implementation ต่างกัน
- Maintainability: แก้ Implementation แทนได้โดยไม่กระทบ Consumer
- Centralized Configuration: DI Container จัดการ Lifetime/Registration ที่เดียว
ข้อเสีย/ความท้าทาย
- Complexity เพิ่มขึ้น (Container, Registration, Lifetime)
- Constructor Over-injection (Parameter 過多) → Refactor to Facade/Aggregate Service
- Runtime Errors หาก Registration ผิด (Compile-time ไม่จับได้)
- Performance Overhead น้อย (Container Resolution)
DI ใน ASP.NET Core (Built-in Container)
// Program.cs
builder.Services.AddScoped<IEmailService, SmtpEmailService>();
builder.Services.AddSingleton<ILogger, SerilogLogger>();
builder.Services.AddTransient<OrderService>(); // Concrete Type ก็ได้
// Controller ใช้ Constructor Injection
public class OrdersController : ControllerBase
{
private readonly OrderService _orderService;
public OrdersController(OrderService orderService) => _orderService = orderService;
}
Lifetime Options
Transient: New Instance ทุกครั้งที่ RequestScoped: One Instance ต่อ Request (HTTP Request)Singleton: One Instance ตลอด Application Lifetime