2012-06-13 70 views
6

我想在渲染之前獲取視圖任何部分的最後修改時間。這包括佈局頁,局部視圖等ASP.NET MVC3獲取最後修改時間的任何部分的視圖?

我想設置一個適當的時間

Response.Cache.SetLastModified(viewLastWriteUtcTime); 

妥善處理HTTP緩存。目前,我有這方面的工作視圖本身不過如果在佈局網頁那些不被

var viewLastWriteUtcTime = System.IO.File.GetLastWriteTime(
    Server.MapPath(
    (ViewEngines.Engines.FindView(ControllerContext, ViewBag.HttpMethod, null) 
      .View as BuildManagerCompiledView) 
     .ViewPath)).ToUniversalTime(); 

拿起任何改變,或兒童的部分看法有什麼辦法,我可以得到總最後修改時間?

我不想在修改視圖的相關部分的部署之後以304 Not Modified迴應,因爲用戶會得到不一致的行爲。

+0

有趣的問題。不知道我的解決方案是否是最簡單的方法,但捅入它很有趣。 – tvanfosson

回答

5

我不會保證這是最有效的方法,但我已經測試過它,它的工作原理。您可能需要調整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 
{ 
    ... 
} 
+0

對我來說看起來不錯,我可能會使用緩存而不是會話,並將它們視爲基於視圖名稱或類似名稱。有意義的是,掛鉤到創建視圖/部分將是一個可行的方法來做到這一點,然後使用後備存儲,如果寫入時間超過當前最大值,則會得到更新。 –

相關問題