2012-06-13 22 views
6

Tôi muốn nhận được thời gian sửa đổi lần cuối của bất kỳ phần nào của chế độ xem trước khi hiển thị. Điều này bao gồm các trang bố trí, quang cảnh một phần, vvASP.NET MVC3 Nhận thời gian sửa đổi lần cuối của bất kỳ phần nào của chế độ xem?

Tôi muốn thiết lập một thời điểm thích hợp cho

Response.Cache.SetLastModified(viewLastWriteUtcTime); 

để xử lý đúng đắn http bộ nhớ đệm. Hiện nay tôi đã này làm việc cho quan điểm riêng của mình tuy nhiên nếu có bất kỳ thay đổi trong các trang bố trí, hoặc xem con một phần những người không được nhặt bởi

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

Có cách nào tôi có thể nhận tổng thời gian sửa đổi lần cuối?

Tôi không muốn trả lời với 304 Not Modified sau khi triển khai đã sửa đổi một phần liên quan của chế độ xem khi người dùng sẽ nhận được hành vi không nhất quán.

+0

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

Trả lời

5

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 
{ 
    ... 
} 
+0

Có vẻ tốt với tôi, tôi có thể sử dụng bộ nhớ cache thay vì phiên và khóa chúng dựa trên tên chế độ xem hoặc tương tự. Làm cho tinh thần rằng hooking vào tạo ra xem/một phần sẽ là một cách khả thi để làm điều này và sau đó sử dụng một cửa hàng sao lưu được cập nhật nếu thời gian ghi vượt quá tối đa hiện hành. –