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.Cache và HttpContext.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.