これが最も効果的な方法であるとは保証しませんが、テストして動作します。 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());
...
}
であなたの新しいものと既存のエンジン(複数可)を交換するかにつき、コントローラベースでOnResultExecutedを追加します。注:私は、応答が送信された後にコントローラのOnResultExecutedが実行されると考えているので、フィルタを使用する必要があります。私のテストは、これが正しいことを示しています。
また、タイムスタンプでセッションを汚染しないようにするために、セッションの値をクリアしています。キャッシュに保存し、そのセッションで短い保存期限を設定して、明示的にクリーンアップする必要がないようにするか、セッションがメモリに保持されないようにして、セッションに格納するトランザクションのコストを回避できます。
[UpdateLastModifiedFromViews]
public class HomeController : Controller
{
...
}
興味深い問題:
最後に、あなたが上またはグローバルフィルタとして使用したいコントローラにフィルタを適用します。私のソリューションが最も簡単な方法であるかどうかはわかりませんが、それを突き刺すのは楽しいことでした。 – tvanfosson