PlusMagi's Blog By Pitt Phunsanit Backend,Database .NET Dependency Injection (DI) & Configuration: จัดการ Lifecycle (Transient, Scoped, Singleton) และ Options Pattern

.NET Dependency Injection (DI) & Configuration: จัดการ Lifecycle (Transient, Scoped, Singleton) และ Options Pattern

ในยุคที่ระบบซอฟต์แวร์มีความซับซ้อนและมีการเชื่อมต่อกับฐานข้อมูลอย่าง PostgreSQL อย่างต่อเนื่อง การจัดการโครงสร้างโค้ดให้มีความยืดหยุ่น (Flexible) และง่ายต่อการบำรุงรักษา (Maintainable) จึงเป็นสิ่งสำคัญอย่างยิ่ง หัวใจสำคัญของการออกแบบที่ทันสมัยคือการลดการผูกติด (Decoupling) ระหว่างส่วนประกอบต่างๆ

บทความนี้จะเจาะลึกถึงหลักการของ Dependency Injection (DI) ซึ่งเป็นรูปแบบการออกแบบ (Design Pattern) ที่ช่วยให้เราสามารถส่งมอบ (Inject) ส่วนประกอบที่คลาสหนึ่งต้องการจากภายนอก แทนที่ให้คลาสนั้นต้องไปสร้างมันขึ้นมาเอง ทำให้โค้ดของเราสามารถทำงานร่วมกับ PostgreSQL ได้อย่างมีประสิทธิภาพและลดความเปราะบางของระบบโดยรวม

💡 Dependency Injection (DI): หัวใจของการออกแบบที่ยืดหยุ่น

DI คือกลไกที่ทำให้คลาสของเราไม่จำเป็นต้องรู้ว่าส่วนประกอบที่มันใช้ทำงานอย่างไร เพียงแค่รู้ว่ามันมีอินเทอร์เฟซ (Interface) อะไรที่ต้องใช้เท่านั้น เมื่อเราใช้ DI ใน .NET เราจะลงทะเบียน (Register) บริการต่างๆ เข้ากับ Container และให้ Container เป็นผู้รับผิดชอบในการสร้างและส่งมอบ Object ที่ถูกต้องตามที่ต้องการ

🔄 การจัดการวงจรชีวิต (Service Lifecycles)

การเลือกวงจรชีวิตที่เหมาะสมสำหรับ Service ต่างๆ เป็นสิ่งสำคัญอย่างยิ่งในการเชื่อมต่อกับฐานข้อมูล PostgreSQL เพราะมันกำหนดว่า Object นั้นจะถูกสร้างและทำลายเมื่อไหร่:

  • Transient: สร้าง Object ใหม่ทุกครั้งที่มีการร้องขอ (Request) เหมาะสำหรับ Service ที่ไม่มีสถานะ (Stateless) เช่น Repository ที่ใช้ Query ข้อมูลจาก PostgreSQL
  • Scoped: สร้าง Object ใหม่เพียงครั้งเดียวต่อขอบเขตการทำงาน (Scope) เช่น หนึ่ง HTTP Request เหมาะสำหรับ DbContext ที่ควรมีสถานะการเชื่อมต่อ (Connection State) เดียวตลอด Request นั้น
  • Singleton: สร้าง Object เพียงครั้งเดียวตลอดอายุการทำงานของแอปพลิเคชัน เหมาะสำหรับ Service ที่เป็น Global Cache หรือ Logger

⚙️ Options Pattern: การจัดการ Configuration อย่างเป็นระบบ

เมื่อแอปพลิเคชันของเราต้องเชื่อมต่อกับ PostgreSQL เราจำเป็นต้องจัดการค่า Configuration ต่างๆ เช่น Connection String, Timeout, หรือค่าพารามิเตอร์อื่นๆ Options Pattern ช่วยให้เราสามารถอ่านค่าเหล่านี้จากไฟล์ภายนอก (เช่น appsettings.json) และผูกมันเข้ากับ Class อย่างเป็นระเบียบ ทำให้โค้ดของเราสามารถปรับเปลี่ยนค่าต่างๆ ได้โดยไม่ต้องแก้ไขโค้ดหลักเลย

💻 ตัวอย่างการลงทะเบียน Service และ Configuration

ในโค้ดจริง เราจะทำการลงทะเบียน Service ต่างๆ และค่า Configuration ดังตัวอย่าง:

C#

// 1. การลงทะเบียน Service และ Lifecycle ใน Program.cs หรือ Startup.cs
services.AddScoped<IConnectionService>(), typeof(PostgresConnectionService); // Scoped สำหรับ DbContext
services.AddTransient<IQueryService>(), typeof(SimpleQueryService); // Transient สำหรับ Query ที่ไม่เก็บสถานะ
services.AddSingleton<ILogger>(), typeof(DatabaseLogger); // Singleton สำหรับ Logger

// 2. การผูกค่า Configuration (Options Pattern)
services.Configure<PostgresOptions>(config =>
{
    config.ConnectionString = "Host=localhost;Database=mydb;User=user;Password=pass;";
    config.TimeoutSeconds = 30;
});

💡 การใช้งานใน Business Logic

เมื่อเราต้องการใช้ Service ที่มีการตั้งค่า Connection String เราสามารถ Inject Options เข้าไปใน Constructor ได้:

C#

public class DataService
{
    private readonly PostgresOptions _options;

    // DI Inject ทั้ง Service และ Options
    public DataService(IOptions<PostgresOptions> options)
    {
        _options = options.Value;
    }

    public void ExecuteQuery(string[] ids) // ใช้สตริงสั้น 3 ค่า
    {
        // ใช้ ConnectionString จาก Options และส่งค่า '1,2,3' ไป Query PostgreSQL
        Console.WriteLine($"Connecting to {this._options.ConnectionString} for IDs: {string.Join(",", ids)}");
    }
}

✅ บทสรุป

การทำความเข้าใจและใช้ประโยชน์จาก Dependency Injection, การเลือก Lifecycle ที่ถูกต้อง (Transient, Scoped, Singleton), และการใช้ Options Pattern อย่างเป็นระบบ ไม่เพียงแต่ช่วยให้โค้ดของเรามีความสะอาด (Clean Code) เท่านั้น แต่ยังช่วยให้เราสร้างแอปพลิเคชันที่เชื่อมต่อกับ PostgreSQL ได้อย่างมีประสิทธิภาพสูง สามารถทดสอบ (Testable) ได้ง่าย และพร้อมสำหรับการขยายขนาดในอนาคตอย่างแท้จริง