Phân Tích Kỹ Thuật HttpRuntime.Cache Và HttpContext.Current.Cache Trong ASP.NET

Giới thiệu về cơ chế lưu trữ tạm thời

Trong quá trình phát triển ứng dụng ASP.NET, việc quản lý bộ nhớ đệm (cache) đóng vai trò quan trọng để tối ưu hóa hiệu suất. Hai thuộc tính thường được nhắc đến để truy cập đối tượng cache là HttpRuntime.CacheHttpContext.Current.Cache. Mặc dù chúng thường được sử dụng thay thế cho nhau trong các ứng dụng web, nhưng bản chất và phạm vi hoạt động của chúng có những điểm khác biệt cần lưu ý.

Phân tích nguồn gốc và định nghĩa

Dựa trên tài liệu kỹ thuật, HttpContext.Current.Cache được định nghĩa là đối tượng Cache dành riêng cho请求 HTTP hiện tại. Trong khi đó, HttpRuntime.Cache cung cấp đối tượng Cache cho toàn bộ ứng dụng đang chạy.

Khi xem xét mã nguồn thông qua các công cụ phân tích assembly, ta có thể thấy cấu trúc triển khai thực tế như sau:

// Triển khai thuộc tính Cache trong HttpContext
public sealed class HttpContext
{
    public Cache Cache
    {
        get
        {
            // Trả về trực đối tượng từ HttpRuntime
            return HttpRuntime.Cache;
        }
    }
}

// Triển khai thuộc tính Cache trong HttpRuntime
public sealed class HttpRuntime
{
    public static Cache Cache
    {
        get
        {
            // Kiểm tra môi trường cài đặt ASP.NET
            if (AspInstallDirectoryInternal == null)
            {
                throw new HttpException("ASP.NET chưa được cài đặt đúng cách");
            }
            
            // Truy xuất đối tượng cache nội bộ
            Cache cacheInstance = _theRuntime._cachePublic;
            if (cacheInstance == null)
            {
                CacheInternal internalCache = CacheInternal;
                CacheSection configSection = RuntimeConfig.GetAppConfig().Cache;
                internalCache.ReadCacheInternalConfig(configSection);
                _theRuntime._cachePublic = internalCache.CachePublic;
                cacheInstance = _theRuntime._cachePublic;
            }
            return cacheInstance;
        }
    }
}

Từ đoạn mã trên, có thể khẳng định rằng HttpContext.Current.Cache thực chất chỉ là một wrapper gọi đến HttpRuntime.Cache. Cả hai đều trỏ về cùng một đối tượng bộ nhớ đệm duy nhất của ứng dụng. Sự khác biệt nằm ở ngữ cảnh truy cập: HttpRuntime cung cấp dịch vụ runtime cho ứng dụng nên có thể dùng ở nhiều nơi, còn HttpContext chỉ tồn tại khi có một request HTTP cụ thể.

Minh chứng qua ứng dụng Console

Để kiểm chứng phạm vi hoạt động, ta có thể thử nghiệm trong một ứng dụng Console. Vì môi trường Console không có request HTTP nên HttpContext.Current sẽ trả về null.

static void Main(string[] arguments)
{
    // Thử truy cập HttpRuntime.Cache
    var appCache = System.Web.HttpRuntime.Cache;
    string dataKey = "RuntimeData";
    appCache.Insert(dataKey, "Dữ liệu lưu từ HttpRuntime");

    if (appCache != null)
    {
        Console.WriteLine($"Kết quả từ HttpRuntime: {appCache[dataKey]}");
    }

    // Thử truy cập HttpContext.Current
    var webContext = System.Web.HttpContext.Current;
    if (webContext == null)
    {
        Console.WriteLine("Không thể truy cập HttpContext trong ứng dụng Console");
    }
    else
    {
        var contextCache = webContext.Cache;
        contextCache.Insert("ContextData", "Dữ liệu lưu từ HttpContext");
    }
    
    Console.ReadLine();
}

Kết quả thực thi cho thấy HttpRuntime.Cache vẫn hoạt động bình thường ngoài môi trường web, trong khi HttpContext bị vô hiệu hóa. Do đó, nếu mục tiêu là sử dụng cache cho toàn ứng dụng mà không phụ thuộc vào request, HttpRuntime.Cache là lựa chọn tối ưu hơn.

Triển khai lớp quản lý Cache thống nhất

Dưới đây là mẫu lớp tiện ích giúp thao tác với cache một cách an toàn, hỗ trợ cả việc kiểm tra ngữ cảnh hiện tại và thiết lập các tham số hết hạn.

public class CacheManager
{
    // Lưu trữ chuỗi với thời gian sống sliding expiration
    public static void StoreStringContent(string identifier, string content)
    {
        if (System.Web.HttpContext.Current != null)
        {
            var timeSpan = TimeSpan.FromMinutes(5);
            System.Web.HttpContext.Current.Cache.Insert(
                identifier, 
                content, 
                null, 
                System.Web.Caching.Cache.NoAbsoluteExpiration, 
                timeSpan
            );
        }
    }

    // Lưu trữ đối tượng DataTable
    public static void StoreTableData(string identifier, DataTable tableData)
    {
        if (System.Web.HttpContext.Current != null)
        {
            System.Web.HttpContext.Current.Cache.Insert(identifier, tableData);
        }
    }

    // Truy xuất chuỗi
    public static string RetrieveStringContent(string identifier)
    {
        if (System.Web.HttpContext.Current != null && 
            System.Web.HttpContext.Current.Cache[identifier] != null)
        {
            return System.Web.HttpContext.Current.Cache[identifier].ToString();
        }
        return string.Empty;
    }

    // Truy xuất DataTable
    public static DataTable RetrieveTableData(string identifier)
    {
        if (System.Web.HttpContext.Current != null && 
            System.Web.HttpContext.Current.Cache[identifier] != null)
        {
            return System.Web.HttpContext.Current.Cache[identifier] as DataTable;
        }
        return null;
    }

    // Xóa một mục cụ thể
    public static void InvalidateItem(string identifier)
    {
        if (System.Web.HttpContext.Current != null)
        {
            System.Web.HttpContext.Current.Cache.Remove(identifier);
        }
    }

    // Hiển thị danh sách các key đang tồn tại
    public static string ListAllKeys()
    {
        var output = new StringBuilder();
        var enumerator = System.Web.HttpRuntime.Cache.GetEnumerator();
        
        while (enumerator.MoveNext())
        {
            output.AppendLine($"Key: {enumerator.Key}");
        }
        
        return $"Tổng số mục: {System.Web.HttpRuntime.Cache.Count}\n{output.ToString()}";
    }

    // Xóa toàn bộ cache (cần cẩn trọng khi dùng trong production)
    public static void PurgeAllCache()
    {
        var cacheInstance = System.Web.HttpRuntime.Cache;
        var keysList = new ArrayList();
        var enumerator = cacheInstance.GetEnumerator();
        
        while (enumerator.MoveNext())
        {
            keysList.Add(enumerator.Key);
        }

        foreach (string key in keysList)
        {
            cacheInstance.Remove(key);
        }
    }
}

Sử dụng trong WebForm

Trong môi trường WebForm, cả hai cách đều hoạt động. Tuy nhiên, việc sử dụng lớp wrapper như trên sẽ giúp code gọn gàng hơn. Dưới đây là ví dụ xử lý sự kiện click button để tương tác với cache.

protected void btnAction_Click(object sender, EventArgs e)
{
    // Lưu dữ liệu mới
    CacheManager.StoreStringContent("INFO_COMPANY", "Công ty Công Nghệ Mới");
    
    // Hiển thị danh sách cache hiện có
    txtDisplay.Text = CacheManager.ListAllKeys();
    
    // Lấy dữ liệu vừa lưu
    string companyInfo = CacheManager.RetrieveStringContent("INFO_COMPANY");
    txtDisplay.Text += $"\nThông tin: {companyInfo}";
}

Ưu tiên loại bỏ mục cache (CacheItemPriority)

Khi bộ nhớ server cạn kiệt, ASP.NET sẽ tự động dọn dẹp cache. Thuộc tính CacheItemPriority cho phép developer chỉ định mức độ quan trọng của từng mục dữ liệu để quyết định thứ bị xóa trước.

public enum CacheItemPriority
{
    // Khả năng bị xóa cao nhất khi cần giải phóng bộ nhớ
    Low = 1,

    // Khả năng bị xóa cao hơn mức Normal
    BelowNormal = 2,

    // Mức mặc định, khả năng bị xóa trung bình
    Normal = 3,

    // Khả năng bị xóa thấp hơn mức Normal
    AboveNormal = 4,

    // Khả năng bị xóa thấp nhất
    High = 5,

    // Không bị xóa tự động do thiếu bộ nhớ (chỉ xóa khi hết hạn)
    NotRemovable = 6
}

Việc thiết lập đúng mức ưu tiên giúp đảm bảo các dữ liệu quan trọng luôn sẵn sàng trong bộ nhớ đệm ngay cả khi hệ thống chịu áp lực tải cao.

Thẻ: asp.net caching-mechanism httpcontext httpruntime system.web

Đăng vào ngày 23 tháng 5 lúc 03:50