อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton
เรียนรู้ว่าอายุการใช้งานแบบ Transient, Scoped และ Singleton ส่งผลต่อการสร้าง การใช้ร่วมกัน และการกำจัดออบเจ็กต์อย่างไร
อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดอายุการใช้งานจึงสำคัญ
อายุการใช้งานของบริการควบคุมทั้ง ระยะเวลาที่อินสแตนซ์มีชีวิตอยู่ และ จำนวนอินสแตนซ์ ที่ถูกสร้างขึ้น การเลือกอายุการใช้งานผิดอาจทำให้เกิดข้อผิดพลาดที่ตรวจพบได้ยาก เช่น ข้อมูลรั่วไหลระหว่างคำขอ หรือการสร้างอ็อบเจ็กต์มากเกินไป
ชั่วคราว: สร้างใหม่ทุกครั้ง
บริการแบบ ชั่วคราว จะถูกสร้างขึ้นใหม่ทุกครั้งที่มีการร้องขอจากคอนเทนเนอร์ ให้ใช้กับบริการที่มีน้ำหนักเบาและไม่มีสถานะ ซึ่งการใช้สถานะร่วมกันอาจก่อให้เกิดอันตราย
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Each resolution creates a new instance:
// var a = sp.GetRequiredService<IEmailSender>(); // new
// var b = sp.GetRequiredService<IEmailSender>(); // new (different)ตามขอบเขต: หนึ่งครั้งต่อคำขอ
บริการแบบ ตามขอบเขต จะถูกสร้างขึ้นหนึ่งครั้งต่อ scope ซึ่งโดยทั่วไปหมายถึงคำขอ HTTP หนึ่งรายการใน ASP.NET Core อินสแตนซ์เดียวกันจะถูกใช้ร่วมกันภายในคำขอหนึ่งรายการ และจะสร้างอินสแตนซ์ใหม่สำหรับคำขอถัดไป
builder.Services.AddScoped<AppDbContext>();
// Within a single HTTP request:
// Both resolve to the SAME AppDbContext instance
// -> consistent unit of work across the requestซิงเกิลตัน: หนึ่งอินสแตนซ์ตลอดอายุแอป
บริการแบบ ซิงเกิลตัน จะถูกสร้างขึ้นเพียงครั้งเดียวและนำกลับมาใช้ตลอดอายุการใช้งานของแอป ให้ใช้กับบริการที่สร้างได้ยากและปลอดภัยต่อการทำงานหลายเธรด เช่น แคชการกำหนดค่า
builder.Services.AddSingleton<IMemoryCache, MemoryCache>();
// Or register a pre-built instance:
var config = new AppConfig { MaxRetries = 3 };
builder.Services.AddSingleton<IAppConfig>(config);เปรียบเทียบทั้งสามแบบ
เปรียบเทียบแบบเห็นภาพได้อย่างรวดเร็ว:
- ชั่วคราว — อินสแตนซ์ใหม่ทุกคำขอ เหมาะกับยูทิลิตีที่ไม่มีสถานะ
- ตามขอบเขต — หนึ่งอินสแตนซ์ต่อคำขอ HTTP เหมาะกับ DbContext และหน่วยงานต่อหนึ่งงาน
- ซิงเกิลตัน — หนึ่งอินสแตนซ์ตลอดอายุแอป เหมาะกับแคชและการกำหนดค่า
ปัญหาการพึ่งพาที่ถูกกักไว้
การฉีดบริการแบบ ตามขอบเขต หรือ ชั่วคราว เข้าไปในบริการแบบ ซิงเกิลตัน เป็นข้อผิดพลาดที่พบบ่อย ซิงเกิลตันจะเก็บการอ้างอิงไปยังบริการที่มีอายุสั้น ทำให้บริการนั้นยังคงอยู่เป็นเวลานานเกินไป
// BAD: Singleton captures a Scoped service
public class MySingleton
{
private readonly AppDbContext _db; // Scoped!
public MySingleton(AppDbContext db) => _db = db;
// _db is now stuck alive for the app's lifetime
}การตรวจจับการละเมิดขอบเขต
เปิดใช้ ValidateScopes เพื่อให้คอนเทนเนอร์แสดงข้อผิดพลาดเมื่อเริ่มต้น หากตรวจพบการพึ่งพาที่ถูกกักไว้ วิธีนี้ช่วยตรวจจับข้อผิดพลาดก่อนที่จะทำให้ข้อมูลในระบบจริงเสียหาย
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true; // detect captive deps
options.ValidateOnBuild = true; // fail fast at start
});บริการแบบ Scoped นอกคำขอ
หากต้องการแก้ไขบริการแบบ Scoped ในงานเบื้องหลัง ให้สร้างขอบเขตอย่างชัดเจนโดยใช้ IServiceScopeFactory ห้ามแก้ไขบริการแบบ Scoped โดยตรงจากคอนเทนเนอร์ราก
public class MyWorker : BackgroundService
{
private readonly IServiceScopeFactory _factory;
public MyWorker(IServiceScopeFactory factory) => _factory = factory;
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var scope = _factory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Users.ToListAsync(ct);
}
}บริการแบบ Transient ที่ปล่อยทรัพยากรได้
บริการ IDisposable แบบ Transient ที่แก้ไขจากคอนเทนเนอร์รากจะถูกติดตามจนกว่าคอนเทนเนอร์จะถูกทำลาย ซึ่งมักเกิดขึ้นตอนปิดแอป ให้แก้ไขบริการเหล่านี้ภายในขอบเขตเพื่อคืนทรัพยากรได้อย่างรวดเร็ว
// Correctly scoped transient disposable
using var scope = sp.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<MyDisposableService>();
await svc.DoWorkAsync();
// svc.Dispose() called when scope is disposedการเลือกอายุการใช้งานที่เหมาะสม
แนวทางตัดสินใจอย่างง่าย:
- บริการไม่มีสถานะใช่หรือไม่ → ชั่วคราว
- บริการขึ้นอยู่กับคำขอ เช่น ข้อมูลประจำตัวผู้ใช้หรือ DbContext หรือไม่ → ตามขอบเขต
- บริการปลอดภัยต่อการทำงานหลายเธรดและสร้างได้ยากหรือไม่ → ซิงเกิลตัน
ตัวอย่างจริง: ใช้สามอายุการใช้งานร่วมกัน
เว็บ API ทั่วไปจะลงทะเบียนบริการต่าง ๆ ด้วยอายุการใช้งานที่เหมาะสม เพื่อให้ได้ทั้งความถูกต้องและประสิทธิภาพ
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddScoped<AppDbContext>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<OrderService>();
builder.Services.AddTransient<IEmailSender, SendGridEmailSender>();ตรวจสอบความเข้าใจ
จะเกิดปัญหาใดขึ้นเมื่อบริการแบบซิงเกิลตันเก็บการอ้างอิงไปยังบริการแบบตามขอบเขต
ทบทวน: อายุการใช้งานของบริการ
ประเด็นสำคัญ:
- ชั่วคราว: อินสแตนซ์ใหม่ทุกครั้ง เหมาะกับบริการที่ไม่มีสถานะและมีน้ำหนักเบา
- ตามขอบเขต: หนึ่งอินสแตนซ์ต่อคำขอ เหมาะกับ DbContext คลังข้อมูล และหน่วยงานต่อหนึ่งงาน
- ซิงเกิลตัน: หนึ่งอินสแตนซ์ตลอดไป เหมาะกับแคช การกำหนดค่า และยูทิลิตีที่ปลอดภัยต่อการทำงานหลายเธรด
- ห้ามฉีดบริการอายุสั้นเข้าไปในบริการที่มีอายุยาวกว่า เพราะจะเกิดการพึ่งพาที่ถูกกักไว้
- ใช้
IServiceScopeFactoryเพื่อแก้ไขบริการแบบ Scoped ในงานเบื้องหลัง
คำถามที่พบบ่อย
บทเรียน “อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton”
เรียนรู้ว่าอายุการใช้งานแบบ Transient, Scoped และ Singleton ส่งผลต่อการสร้าง การใช้ร่วมกัน และการกำจัดออบเจ็กต์อย่างไร คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐานคอนเทนเนอร์ DI
- อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton
- การฉีดผ่าน Constructor และอินเทอร์เฟซ
- รูปแบบ Factory และ Options