Non ho intenzione di garantire che questo sia il modo più efficace per farlo, ma l'ho provato e funziona. Potrebbe essere necessario regolare la logica GetRequestKey() e si potrebbe voler scegliere una posizione di memoria temporanea alternativa a seconda del proprio scenario. Non ho implementato alcuna memorizzazione nella cache per i tempi dei file, dal momento che sembrava qualcosa a cui non saresti interessato. Non sarebbe difficile aggiungere se fosse ok avere un po 'di tempo in meno e volevi evitare l'overhead di accesso ai file su ogni richiesta.
Innanzitutto, estendere RazorViewEngine con un motore di visualizzazione che tiene traccia dell'ora massima più recente modificata per tutte le visualizzazioni visualizzate durante questa richiesta. Lo facciamo memorizzando l'ora più recente nella sessione immessa dall'id di sessione e dal timestamp della richiesta. Potresti farlo altrettanto facilmente con qualsiasi altro motore di visualizzazione.
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);
}
}
Avanti, sostituire il motore (s) esistente con quello nuovo in global.asax.cs
protected void Application_Start()
{
System.Web.Mvc.ViewEngines.Engines.Clear();
System.Web.Mvc.ViewEngines.Engines.Add(new ViewEngines.CacheFriendlyRazorViewEngine());
...
}
Infine, in qualche filtro globale o su base per-controller di aggiungere un OnResultExecuted. Nota, credo che OnResultExecuted nel controller funzioni dopo che la risposta è stata inviata, quindi penso che tu debba usare un filtro. I miei test indicano che questo è vero.
Inoltre, si noti che sto cancellando il valore dalla sessione dopo che è stato utilizzato per evitare di inquinare la sessione con i timestamp. Si potrebbe desiderare di tenerlo nella cache e impostare una breve scadenza su di esso in modo da non dover cancellare in modo esplicito le cose o se la sessione non viene tenuta in memoria per evitare i costi di transazione di archiviazione nella sessione.
[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));
}
}
Infine, applicare il filtro per il controller che si desidera utilizzare su o come un filtro globale:
[UpdateLastModifiedFromViews]
public class HomeController : Controller
{
...
}
problema interessante. Non sono sicuro che la mia soluzione sia la via più semplice, ma è stato divertente introdurla. – tvanfosson