Trang chủ Lập trình ASP.NET Core Phân biệt IOptions, IOptionsSnapshot và IOptionsMonitor trong ASP.NET Core: Hướng dẫn chi tiết
Lập trình ASP.NET Core 03/02/2026 19 lượt xem

Phân biệt IOptions, IOptionsSnapshot và IOptionsMonitor trong ASP.NET Core: Hướng dẫn chi tiết

Phân biệt IOptions, IOptionsSnapshot và IOptionsMonitor trong ASP.NET Core: Hướng dẫn chi tiết

Khi làm việc với Options Pattern trong ASP.NET Core, việc map (ánh xạ) file appsettings.json sang class C# chỉ là bước đầu tiên. Câu hỏi quan trọng tiếp theo là: Làm thế nào để inject và sử dụng class đó hiệu quả?

Microsoft cung cấp 3 interface chính để làm việc này:

  1. IOptions<T>

  2. IOptionsSnapshot<T>

  3. IOptionsMonitor<T>

Thoạt nhìn chúng có vẻ giống nhau, nhưng việc chọn sai interface có thể dẫn đến lỗi nghiêm trọng (như memory leak, crash ứng dụng) hoặc ứng dụng không cập nhật cấu hình mới. Bài viết này sẽ giúp bạn hiểu rõ bản chất và sự khác biệt của từng loại.


1. IOptions<T> - Lựa chọn cơ bản nhất

Đây là interface đơn giản nhất và thường được sử dụng mặc định khi bạn mới học về Options Pattern.

Đặc điểm cốt lõi:

  • Lifetime: Singleton.

  • Cơ chế: Khi ứng dụng khởi động, nó đọc file cấu hình một lần duy nhất, bind dữ liệu vào class POCO và lưu vào bộ nhớ.

  • Khả năng cập nhật (Hot-reload): KHÔNG. Nếu bạn sửa file appsettings.json khi ứng dụng đang chạy, giá trị trong IOptions sẽ không thay đổi. Bạn bắt buộc phải restart ứng dụng.

Khi nào nên dùng?

  • Dùng cho các cấu hình tĩnh, không bao giờ thay đổi trong suốt vòng đời ứng dụng (ví dụ: Domain name, thông tin license, các hằng số hệ thống).

  • Khi bạn cần hiệu năng cao nhất (vì không tốn chi phí đọc lại file).

C#

// Đăng ký (Program.cs)
builder.Services.Configure<MyConfig>(builder.Configuration.GetSection("MyConfig"));

// Sử dụng
public class HomeController : Controller
{
    private readonly MyConfig _config;
    
    // Inject IOptions<T>
    public HomeController(IOptions<MyConfig> options)
    {
        _config = options.Value; // Lấy giá trị
    }
}

2. IOptionsSnapshot<T> - Cập nhật theo Request

Nếu bạn muốn thay đổi cấu hình mà không cần restart server, IOptionsSnapshot là giải pháp đầu tiên.

Đặc điểm cốt lõi:

  • Lifetime: Scoped.

  • Cơ chế: Dữ liệu cấu hình được tính toán lại mỗi khi có một Request mới.

  • Khả năng cập nhật (Hot-reload): .

  • Tính nhất quán (Consistency): Trong cùng một HTTP Request, giá trị của nó là bất biến. Dù bạn gọi nó ở 10 service khác nhau trong cùng 1 request, nó vẫn trả về cùng 1 giá trị (kể cả khi file config bị sửa giữa chừng quá trình xử lý request đó).

Khi nào nên dùng?

  • Khi bạn cần cấu hình có thể thay đổi "nóng" (ví dụ: bật/tắt tính năng bằng Feature Flag, thay đổi tỷ lệ khuyến mãi).

  • Lưu ý quan trọng: Vì nó là Scoped, bạn không thể inject IOptionsSnapshot vào một service là Singleton. Nếu cố tình làm vậy, ứng dụng sẽ báo lỗi (hoặc gây ra lỗi logic nghiêm trọng).

C#

// Sử dụng
public class OrderService
{
    private readonly MyConfig _config;

    // Inject IOptionsSnapshot<T>
    public OrderService(IOptionsSnapshot<MyConfig> options)
    {
        _config = options.Value; // Giá trị mới nhất tại thời điểm request bắt đầu
    }
}

3. IOptionsMonitor<T> - Theo dõi thời gian thực

Đây là interface mạnh mẽ nhất, dùng cho các kịch bản phức tạp hoặc khi cần inject cấu hình động vào các Singleton service.

Đặc điểm cốt lõi:

  • Lifetime: Singleton.

  • Cơ chế: Nó hoạt động như một "người giám sát". Nó luôn trả về giá trị mới nhất tại thời điểm bạn truy cập thuộc tính .CurrentValue.

  • Khả năng cập nhật (Hot-reload): .

  • Sự kiện (Events): Hỗ trợ OnChange để bắn ra sự kiện khi file cấu hình thay đổi.

Khi nào nên dùng?

  • Khi bạn cần đọc cấu hình mới nhất trong một Singleton Service (nơi mà IOptionsSnapshot không dùng được).

  • Khi bạn cần thực hiện một hành động nào đó ngay khi cấu hình thay đổi (ví dụ: xóa cache khi connection string thay đổi).

C#

public class SingletonService
{
    private readonly IOptionsMonitor<MyConfig> _monitor;

    public SingletonService(IOptionsMonitor<MyConfig> monitor)
    {
        _monitor = monitor;
        
        // Đăng ký sự kiện khi config thay đổi
        _monitor.OnChange(config => {
            Console.WriteLine("Config has changed!");
        });
    }

    public void DoWork()
    {
        // Luôn lấy giá trị mới nhất qua .CurrentValue
        var currentValue = _monitor.CurrentValue; 
    }
}

Bảng so sánh tóm tắt (Cheat Sheet)

Đặc điểmIOptions<T>IOptionsSnapshot<T>IOptionsMonitor<T>LifetimeSingletonScopedSingletonĐọc lại dữ liệuChỉ khi App StartMỗi RequestKhi có thay đổiHot ReloadKhôngCóCóHiệu năngTốt nhấtThấp hơn (do đọc lại)TốtTruy cập giá trị.Value.Value.CurrentValueDùng trong Singleton?KHÔNG


Kết luận: Nên chọn cái nào?

  1. Mặc định hãy dùng IOptions<T> vì nó nhẹ và nhanh nhất.

  2. Nếu cần thay đổi cấu hình mà không restart app, hãy dùng IOptionsSnapshot<T>.

  3. Nếu cần dùng cấu hình động bên trong một Singleton Service, hoặc cần bắt sự kiện OnChange, hãy dùng IOptionsMonitor<T>.

Hiểu rõ 3 class này giúp bạn làm chủ hoàn toàn việc quản lý cấu hình trong .NET Core, tránh được các lỗi "inject Scoped vào Singleton" kinh điển.

Tác giả: Bạch Ngọc Toàn

Chú ý: Tất cả các bài viết trên TEDU.COM.VN đều thuộc bản quyền TEDU, yêu cầu dẫn nguồn khi trích lại trên website khác.

Chia sẻ:

Bài viết liên quan