我不會保證這是最有效的方法,但我已經測試過它,它的工作原理。您可能需要調整GetRequestKey()邏輯,您可能需要根據自己的場景選擇一個備用臨時存儲位置。我沒有對文件時間執行任何緩存,因爲這似乎是您不感興趣的內容。如果可以少量關閉時間並且您希望避免文件訪問開銷在每個請求上。
首先,使用視圖引擎來擴展RazorViewEngine,該視圖引擎會跟蹤此請求期間呈現的所有視圖的最大修改時間。我們通過在由會話ID和請求時間戳鍵控的會話中存儲最新時間來完成此操作。你可以用任何其他視圖引擎輕鬆做到這一點。
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);
}
}
接下來,在的global.asax.cs
protected void Application_Start()
{
System.Web.Mvc.ViewEngines.Engines.Clear();
System.Web.Mvc.ViewEngines.Engines.Add(new ViewEngines.CacheFriendlyRazorViewEngine());
...
}
最後你更換新的現有的發動機(S),在一些全局過濾器或每個控制器基礎上增加一個OnResultExecuted。請注意,我相信控制器中的OnResultExecuted在響應發送後運行,所以我認爲您必須使用過濾器。我的測試表明這是真實的。
此外,請注意,在清除使用時間戳的會話之後,我將清除會話中的值。您可能希望將其保存在緩存中併爲其設置一個短期過期,因此您不必明確清理所有內容或者將會話保存在內存中,以避免將其存儲在會話中的交易成本。
[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));
}
}
最後,過濾器適用於你想使用它或作爲一個全局過濾器控制器:
[UpdateLastModifiedFromViews]
public class HomeController : Controller
{
...
}
有趣的問題。不知道我的解決方案是否是最簡單的方法,但捅入它很有趣。 – tvanfosson