Tôi sẽ không đảm bảo rằng đây là cách hiệu quả nhất để làm điều đó, nhưng tôi đã thử nghiệm nó và nó hoạt động. Bạn có thể cần phải điều chỉnh logic GetRequestKey() và bạn có thể muốn chọn một vị trí lưu trữ tạm thời thay thế tùy thuộc vào kịch bản của bạn. Tôi đã không thực hiện bất kỳ bộ nhớ đệm cho thời gian tập tin kể từ đó có vẻ như một cái gì đó bạn sẽ không được quan tâm. Nó sẽ không khó để thêm nếu nó là ok để có thời gian được một số tiền nhỏ và bạn muốn tránh phí truy cập tệp trên mọi yêu cầu.
Đầu tiên, mở rộng RazorViewEngine bằng công cụ xem theo dõi thời gian sửa đổi lần cuối lớn nhất cho tất cả các chế độ xem được hiển thị trong yêu cầu này. Chúng tôi thực hiện việc này bằng cách lưu trữ thời gian mới nhất trong phiên được khóa theo id phiên và dấu thời gian yêu cầu. Bạn có thể dễ dàng thực hiện việc này với bất kỳ công cụ xem nào khác.
public class CacheFriendlyRazorViewEngine : RazorViewEngine
{
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
UpdateLatestTime(controllerContext, GetLastModifiedForPath(controllerContext, viewPath));
var pathToMaster = masterPath;
if (string.IsNullOrEmpty(pathToMaster))
{
pathToMaster = "~/Views/Shared/_Layout.cshtml"; // TODO: derive from _ViewStart.cshtml
}
UpdateLatestTime(controllerContext, GetLastModifiedForPath(controllerContext, pathToMaster));
return base.CreateView(controllerContext, viewPath, masterPath);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
UpdateLatestTime(controllerContext, GetLastModifiedForPath(controllerContext, partialPath));
return base.CreatePartialView(controllerContext, partialPath);
}
private DateTime GetLastModifiedForPath(ControllerContext controllerContext, string path)
{
return System.IO.File.GetLastWriteTime(controllerContext.HttpContext.Server.MapPath(path)).ToUniversalTime();
}
public static void ClearLatestTime(ControllerContext controllerContext)
{
var key = GetRequestKey(controllerContext.HttpContext);
controllerContext.HttpContext.Session.Remove(key);
}
public static DateTime GetLatestTime(ControllerContext controllerContext, bool clear = false)
{
var key = GetRequestKey(controllerContext.HttpContext);
var timestamp = GetLatestTime(controllerContext, key);
if (clear)
{
ClearLatestTime(controllerContext);
}
return timestamp;
}
private static DateTime GetLatestTime(ControllerContext controllerContext, string key)
{
return controllerContext.HttpContext.Session[key] as DateTime? ?? DateTime.MinValue;
}
private void UpdateLatestTime(ControllerContext controllerContext, DateTime timestamp)
{
var key = GetRequestKey(controllerContext.HttpContext);
var currentTimeStamp = GetLatestTime(controllerContext, key);
if (timestamp > currentTimeStamp)
{
controllerContext.HttpContext.Session[key] = timestamp;
}
}
private static string GetRequestKey(HttpContextBase context)
{
return string.Format("{0}-{1}", context.Session.SessionID, context.Timestamp);
}
}
Tiếp theo, thay thế các động cơ hiện có (s) với cái mới của bạn trong global.asax.cs
protected void Application_Start()
{
System.Web.Mvc.ViewEngines.Engines.Clear();
System.Web.Mvc.ViewEngines.Engines.Add(new ViewEngines.CacheFriendlyRazorViewEngine());
...
}
Cuối cùng, trong một số bộ lọc toàn cầu hoặc trên một cơ sở cho mỗi điều khiển thêm một OnResultExecuted. Lưu ý, tôi tin rằng OnResultExecuted trong bộ điều khiển chạy sau khi phản ứng đã được gửi, vì vậy tôi nghĩ rằng bạn phải sử dụng một bộ lọc. Thử nghiệm của tôi cho thấy điều này là đúng.
Ngoài ra, lưu ý rằng tôi đang xóa giá trị trong phiên sau khi nó được sử dụng để tránh gây ô nhiễm phiên với dấu thời gian. Bạn có thể muốn giữ nó trong Cache và thiết lập một thời gian ngắn trên nó, do đó bạn không cần phải dọn sạch mọi thứ một cách rõ ràng hoặc nếu phiên của bạn không được lưu giữ trong bộ nhớ để tránh chi phí giao dịch lưu trữ nó trong phiên.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class UpdateLastModifiedFromViewsAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var cache = filterContext.HttpContext.Response.Cache;
cache.SetLastModified(CacheFriendlyRazorViewEngine.GetLatestTime(filterContext.Controller.ControllerContext, true));
}
}
Cuối cùng, áp dụng các bộ lọc để bộ điều khiển bạn muốn sử dụng nó trên hoặc như một bộ lọc toàn cầu:
[UpdateLastModifiedFromViews]
public class HomeController : Controller
{
...
}
Thú vị vấn đề. Không chắc chắn nếu giải pháp của tôi là cách dễ nhất, nhưng thật thú vị khi được chọc vào đó. – tvanfosson