ASP.NET MVC3获取上次修改时间的任何部分视图?

Chr*_*sic 6 view last-modified http-caching razor asp.net-mvc-3

我希望在渲染之前获取视图任何部分的最后修改时间.这包括布局页面,部分视图等.

我想为自己设定一个合适的时间

 Response.Cache.SetLastModified(viewLastWriteUtcTime);   
Run Code Online (Sandbox Code Playgroud)

正确处理http缓存.目前我有这个工作用于视图本身,但是如果布局页面有任何更改,或子视图部分视图没有被选中

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

有什么方法可以获得整个上次修改时间吗?

304 Not Modified在修改视图的相关部分的部署之后,我不想回应,因为用户会得到不一致的行为.

tva*_*son 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,使用global.asax.cs中的新引擎替换现有引擎

protected void Application_Start()
{
     System.Web.Mvc.ViewEngines.Engines.Clear();
     System.Web.Mvc.ViewEngines.Engines.Add(new ViewEngines.CacheFriendlyRazorViewEngine());
     ...
}
Run Code Online (Sandbox Code Playgroud)

最后,在某些全局过滤器或基于每个控制器的基础上添加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));
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,将过滤器应用于要在其上使用的控制器或作为全局过滤器:

[UpdateLastModifiedFromViews]
public class HomeController : Controller
{
    ...
}
Run Code Online (Sandbox Code Playgroud)