บทความชุด .NET 6 แบบจับมือทำ — #7:Dependency Injection & Service Lifetime

Ponggun
T. T. Software Solution
4 min readFeb 18, 2023

Dependency Injection

ก่อนอื่นเรามาทำความรู้จักกับ Dependency Non-Injection กันก่อนนะครับ ก็คือการที่ Class นึงมีการเรียกใช้อีก Class นึง

ซึ่งเราจะต้องใช้คำสั่ง New เพื่อที่จะสร้าง Object ของอีก Class มาใช้งาน ตรงนี้ละที่เราเรียกว่า Dependency เพราะมีการพึ่งพา Class อื่นเกิดขึ้น

ทีนี้การใช้คำสั่ง New ทำให้โครงสร้างของโปรแกรมเราไม่ยืดหยุ่น ปรับแต่งต่อยากครับ เพราะ Class แต่ละ Class สัมพันธ์กันแบบแน่นแยกออกจากกันลำบาก (High Coupling)

ardalis.com/new-is-glue/

ลองดูตัวอย่างนี้นะครับ

ในตัวอย่างข้างบนจะพบว่า NewClass นั้นผูกติดกับ EmailService แน่นเลย เพราะไปสั่ง New ที่ Constructor ถ้าเราอยากจะเปลี่ยนเป็น Class อื่นก็จะกระทบ NewClass อีก

Dependency Injection (DI) คือการที่เราเลือก Inject Dependency เข้ามาทาง Constructor แทนที่จะ New ใน Class เหมือนเดิม ซึ่งจะมีข้อดีคือ Class ทั้งสองมีความยืดหยุ่นขึ้นมาก NewClass สามารถที่จะใช้งาน Class อื่นแทนได้โดยที่ไม่กระทบ Code เดิมเลยครับ

จากตัวอย่างจะเห็นได้ว่า เรียก Class เดิม Function เดิม แต่ได้ผลลัพธ์ที่แตกต่างกันครับผม เท่ดีเนอะ เย้

ศึกษาเพิ่มเติมที่นี้นะครับ คุณสมเกียรติสรุปไว้ดีมากเลยครับ

Service Lifetime

Key Concepts

ก่อนที่จะอธิบายเรื่องของ Service Lifetime ผมอยากกลับมาทบทวนคำศัพท์ที่เกี่ยวข้องทั้งหมดก่อนนะครับ

ขอบคุณภาพจาก somkiat.cc/dependency-injection-vs-dependency-inversion/
  • Dependency Inversion Principle: คือ Software Design Principle ที่แนะนำชี้ให้เห็นถึงปัญหาเกี่ยวกับเรื่องของ Dependency แต่ไม่ได้บอกว่าใช้เทคนิคอะไรในการแก้ไขปัญหา
  • Inversion of Control (IoC): คือการนำ Dependency Inversion Principle มากำหนดแนวทางการพัฒนาให้ Components อ้างอิงกับ Abstraction แทนการอ้างอิงกับ Concrete Implementation.
  • Dependency Injection (DI): คือ Design Patternในการนำ IoC มาพัฒนา โดยจะทำการ Inject Concrete Implementation ระหว่าง Components
  • IoC Container (DI Container): คือ Programming Framework ที่ช่วยทำ DI ให้โดยอัฒโนมัติ ซึ่งใน ASP.NET Core นั้นมี Build-In IoC Container (DI Container) ให้เราพร้อมใช้ได้อย่างรวดเร็วเลยครับ ช่วยลดขั้นตอน ลด Code ในการพัฒนา DI ลงไปได้เยอะมาก เพียงแต่เราจะต้องเรียนรู้การจัดการ State ของ Components ในขั้นตอนนี้ที่เรียกว่า Service Lifetimes

Service Lifetime Types

คือการจัดการกับอายุของ Instance ที่เราใช้งานแต่ละ Components ในขบวนการทำ DI โดย DI Container ซึ่งจะแบ่งออกเป็น 3 หัวข้อดังนี้

1.Transient

  • Instance จะถูกสร้างใหม่ทุกครั้งที่มีการเรียกใช้งานภายในระบบ
  • เหมาะสำหรับการทำงานที่ Lightweight, Stateless
  • เราสามารถใช้คำสั่ง AddTransient เพื่อทำ DI แบบ Transient Lifetime ได้ครับ
  • Transient services จะถูก Disposed หลังจากที่จบ 1 Request แล้วเสมอ

2. Scoped

  • Instance จะถูกสร้างใหม่ทุกครั้งที่ Client Request เข้ามานะครับ (1 Connection = 1 Client Request)
  • เหมาะสำหรับการทำงานที่ต้องการเรื่องของ Stateful เช่น Entity Framework (AddDbContext)
  • เราสามารถใช้คำสั่ง AddScoped เพื่อทำ DI แบบ Scoped Lifetime ได้ครับ
  • Scoped services จะถูก Disposed หลังจากที่จบ 1 Client Request แล้วเสมอ

3. Singleton

  • Instance จะถูกสร้างแค่ครั้งแรกที่ถูกเรียก หลังจากนั้น Instance จะคงอยู่ตลอดไปจนกว่าเราจะปิดและเปิดระบบขึ้นมาใหม่
  • เหมาะสำหรับบางงานที่ต้องการ Reuse Instance เดิมอยู่ตลอด เช่น Logging, Caching
  • เราสามารถใช้คำสั่ง AddSingleton เพื่อทำ DI แบบ Singleton Lifetime ได้ครับ
ข้อดีของการใช้ DI Container คือช่วย Dispose Instance ให้โดยอัฒโนมัติเลยครับผม

Service Lifetime POC

ถึงเวลามาลองลงมือพิสูจน์การใช้งาน Service Lifetimes กันนะครับ

1.พัฒนา Service Lifetime Interface ใน Core Project

สร้าง Folder Interfaces/Infra/ServiceLifetimes ใน Core Project และสร้าง Files ดังนี้

  • ISingletonService.cs => เพื่อกำหนดมาตราฐานในการทดสอบ Singleton
  • IScopedService.cs => เพื่อกำหนดมาตราฐานในการทดสอบ Scoped
  • ITransientService.cs => เพื่อกำหนดมาตราฐานในการทดสอบ Transient และจะมีการอ้างอิงไปยัง Singleton และ Scoped ด้วยนะครับ

ให้ Copy Code จากลิ้งนี้นะครับ

2.พัฒนา Service Lifetime ใน Infra Project

สร้าง Folder ServiceLifetimes ใน Infra Project และสร้าง Files ดังนี้

  • SingletonService.cs => กำหนดตัวแปรภายใน Class เพื่อใช้นับจำนวนว่ามีการเรียกใช้งาน Function ภายใน Class นี้กี่ครั้งแล้ว (ตั้งใจจะพิสูจน์ว่าค่านี้จะเพิ่มขึ้นเรื่อยๆตามจำนวน Client Request / Client Connection)
  • ScopedService.cs => กำหนดตัวแปรภายใน Class เพื่อใช้นับจำนวนว่ามีการเรียกใช้งาน Function ภายใน Class นี้กี่ครั้งแล้ว (ตั้งใจจะพิสูจน์ว่าค่านี้จะเพิ่มขึ้นเรื่อยๆตามจำนวน System Request)
  • TransientService.cs => กำหนดตัวแปรภายใน Class เพื่อใช้นับจำนวนว่ามีการเรียกใช้งาน Function ภายใน Class นี้กี่ครั้งแล้ว (ตั้งใจจะพิสูจน์ว่าค่าจะไม่เพิ่มขึ้นเลยเพราะการทำงานแบบ Transient จะ Dispose Instance ทุกครั้งที่ทำงานเสร็จแล้ว) นอกจากนั้นยังทดลองเรียกใช้ Singleton, Scoped ใน Class นี้ด้วยเพื่อให้เห็นผลลัพธ์ที่แตกต่างกัน

ให้ Copy Code จากลิ้งนี้นะครับ

3.พัฒนา Service Lifetime ใน API Project

ให้เพิ่ม File ข้างล่างนี้ลงไปยัง Folder Controllers

  • ServiceLifetimesController.cs => ตั้งใจำทำเพื่อทดสอบผ่าน Web API ให้เห็นผลลัพธ์ว่า ทุกครั้งที่ Initiate Instance จะได้ผลลัพธ์ที่แตกต่างกันคือ ถ้าเป็น Transient นั้น แต่ละ Instance จะใช้ State แยกกัน ค่าเลยไม่เพิ่มขึ้นเลย, ถ้าเป็น Scoped นั้น Instance จะใช้ State ร่วมกันใน 1 Client Request ค่าเลยเพิ่มขึ้นได้จนถึงจุดที่จบ Client Connection เท่านั้น พอมี Connection ใหม่ค่าก็เริ่มต้นใหม่, ถ้าเป็น Singleton คือทุก Instances จะใช้ State ร่วมกันตลอด ค่ามันเลยเพิ่มขึ้นเรื่อยๆ

แก้ไข File Program.cs เพื่อติดตั้ง DI Container ด้วย Service Lifetimes ทั้ง 3 ประเภทดังนี้

builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();

ให้ Copy Code จากลิ้งนี้นะครับ

4. ดูผลลัพธ์การทำงาน

4. ดูผลลัพธ์การทำงาน

ผลลัพธ์ออกมาว่าทุกครั้งที่ Initiate Instance จะได้ผลลัพธ์ที่แตกต่างกันคือ ถ้าเป็น Transient นั้น แต่ละ Instance จะใช้ State แยกกัน ค่าเลยไม่เพิ่มขึ้นเลย คงที่ (1), ถ้าเป็น Scoped นั้น Instance จะใช้ State ร่วมกันใน 1 Client Request ค่าเลยเพิ่มขึ้นได้จนถึงจุดที่จบ Client Connection เท่านั้น (1–4) พอมี Connection ใหม่ค่าก็เริ่มต้นใหม่, ถ้าเป็น Singleton คือทุก Instances จะใช้ State ร่วมกันตลอด ค่ามันเลยเพิ่มขึ้นเรื่อยๆ (1-N)

--

--

Ponggun
T. T. Software Solution

Development Manager, Web Developer with ASP.Net, ASP.net Core, Azure and Microsoft Technologies