これが最も効果的な方法であることを保証するつもりはありませんが、テストしたところうまくいきました。場合によっては、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 は、応答が送信された後に実行されると思われるため、フィルターを使用する必要があると思います。私のテストは、これが真実であることを示しています。
また、タイムスタンプでセッションを汚染しないようにするために使用された後、セッションから値をクリアしていることに注意してください。キャッシュに保持し、短い有効期限を設定して、明示的に消去する必要がないようにしたり、セッションに保存するトランザクション コストを回避するためにセッションがメモリに保持されていない場合に使用したい場合があります。
[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
{
...
}