キャッシュされたページに動的コンテンツを追加する (C#)

提供元: Microsoft

動的およびキャッシュされたコンテンツを同じページに混在させる方法について説明します。 キャッシュ後の置換を使用すると、出力キャッシュされたページ内に、バナー広告やニュース項目などの動的コンテンツを表示できます。

出力キャッシュを利用すると、ASP.NET MVC アプリケーションのパフォーマンスを大幅に向上させることができます。 ページが要求されるたびにページを再生成する代わりに、ページを 1 回生成し、複数のユーザーのメモリにキャッシュすることができます。

しかし、問題があります。 ページに動的コンテンツを表示する必要がある場合はどうしますか? たとえば、ページにバナー広告を表示するとします。 すべてのユーザーにまったく同じ広告が表示されるように、バナー広告をキャッシュしないようにする必要があります。 そのような方法でお金を稼いだりしないでしょう。

幸い、簡単な解決策があります。 "キャッシュ後の置換" と呼ばれる ASP.NET フレームワークの機能を利用できます。 キャッシュ後の置換を使用すると、メモリにキャッシュされたページ内の動的コンテンツを置き換えることができます。

通常、[OutputCache] 属性を使用してページを出力キャッシュすると、ページはサーバーとクライアント (Web ブラウザー) の両方にキャッシュされます。 キャッシュ後の置換を使用する場合、ページはサーバー上でのみキャッシュされます。

キャッシュ後の置換の使用

キャッシュ後の置換を使用するには、2 つの手順が必要です。 まず、キャッシュされたページに表示する動的コンテンツを表す文字列を返すメソッドを定義する必要があります。 次に、HttpResponse.WriteSubstitution() メソッドを呼び出して、動的コンテンツをページに挿入します。

たとえば、キャッシュされたページに異なるニュース項目をランダムに表示するとします。 リスト 1 のクラスでは、3 つのニュース項目のリストから 1 つのニュース項目をランダムに返す、RenderNews() という名前の 1 つのメソッドを公開します。

リスト 1 – Models\News.cs

using System;
using System.Collections.Generic;
using System.Web;

namespace MvcApplication1.Models
{
    public class News
    {
        public static string RenderNews(HttpContext context)
        {
            var news = new List<string> 
                { 
                    "Gas prices go up!", 
                    "Life discovered on Mars!", 
                    "Moon disappears!" 
                };
            
            var rnd = new Random();
            return news[rnd.Next(news.Count)];
        }
    }
}

キャッシュ後の置換を利用するには、HttpResponse.WriteSubstitution() メソッドを呼び出します。 WriteSubstitution() メソッドでは、キャッシュされたページの領域を動的コンテンツに置き換えるコードを設定します。 WriteSubstitution() メソッドは、リスト 2 のビューにランダムなニュース項目を表示するために使用されます。

リスト 2 - \Views\Home\Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>

    <% Response.WriteSubstitution(News.RenderNews); %>
        
    <hr />
    
    The content of this page is output cached.
    <%= DateTime.Now %>

    </div>
</body>
</html>

RenderNews メソッドは WriteSubstitution() メソッドに渡されます。 RenderNews メソッドが呼び出されないことに注意してください (かっこはありません)。 代わりに、メソッドへの参照が WriteSubstitution() に渡されます。

インデックス ビューがキャッシュされます。 ビューは、リスト 3 のコントローラーによって返されます。 Index() アクションは [OutputCache] 属性で修飾されているため、インデックス ビューは 60 秒間キャッシュされます。

リスト 3 – Controllers\HomeController.cs

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=60, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }
    }
}

インデックス ビューがキャッシュされている場合でも、[インデックス] ページを要求すると、異なるランダムなニュース項目が表示されます。 [インデックス] ページを要求しても、ページにによって表示される時間は 60 秒間変わりません (図 1 を参照)。 時間が変わらないという事実は、ページがキャッシュされていることを証明します。 ただし、WriteSubstitution() メソッド (ランダムなニュース項目) によって挿入されたコンテンツは、要求ごとに変わります。

図 1 – キャッシュされたページに動的ニュース項目を挿入する

clip_image002

ヘルパー メソッドでのキャッシュ後の置換の使用

キャッシュ後の置換を利用するより簡単な方法は、カスタム ヘルパー メソッド内で WriteSubstitution() メソッドの呼び出しをカプセル化することです。 この方法は、リスト 4 のヘルパー メソッドで示されています。

リスト 4 – AdHelper.cs

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Helpers
{
    public static class AdHelper
    {
        public static void RenderBanner(this HtmlHelper helper)
        {
            var context = helper.ViewContext.HttpContext;
            context.Response.WriteSubstitution(RenderBannerInternal);
        }
        
        private static string RenderBannerInternal(HttpContext context)
        {
            var ads = new List<string> 
                { 
                    "/ads/banner1.gif", 
                    "/ads/banner2.gif", 
                    "/ads/banner3.gif" 
                };

            var rnd = new Random();
            var ad = ads[rnd.Next(ads.Count)];
            return String.Format("<img src='{0}' />", ad);
        }
    }
}

リスト 4 には、RenderBanner() と RenderBannerInternal() の 2 つのメソッドを公開する 静的クラスが含まれています。 RenderBanner() メソッドは、実際のヘルパー メソッドを表します。 このメソッドでは、標準の ASP.NET MVC HtmlHelper クラスを拡張して、他のヘルパー メソッドと同様にビューで Html.RenderBanner() を呼び出すことができるようにします。

RenderBanner() メソッドでは、HttpResponse.WriteSubstitution() メソッドを呼び出し、RenderBannerInternal() メソッドを WriteSubstitution() メソッドに渡します。

RenderBannerInternal() メソッドはプライベート メソッドです。 このメソッドはヘルパー メソッドとして公開されません。 RenderBannerInternal() メソッドでは、3 つのバナー広告画像のリストから 1 つのバナー広告画像をランダムに返します。

リスト 5 の変更されたインデックス ビューは、RenderBanner() ヘルパー メソッドを使用する方法を示しています。 MvcApplication1.Helpers 名前空間をインポートするために、ビューの上部に追加の <%@ Import %> ディレクティブが含まれていることに注意してください。 この名前空間をインポートしない場合、RenderBanner() メソッドは Html プロパティのメソッドとして表示されません。

リスト 5 – Views\Home\Index.aspx (RenderBanner() メソッドを使用)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<%@ Import Namespace="MvcApplication1.Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>

    <% Response.WriteSubstitution(News.RenderNews); %>
    
    <hr />
    
    <% Html.RenderBanner(); %>
    
    <hr />
    
    The content of this page is output cached.
    <%= DateTime.Now %>

    </div>
</body>
</html>

リスト 5 のビューによってレンダリングされたページを要求すると、要求ごとに異なるバナー広告が表示されます (図 2 を参照)。 ページはキャッシュされますが、バナー広告は RenderBanner() ヘルパー メソッドによって動的に挿入されます。

図 2 – ランダムなバナー広告を表示するインデックス ビュー

clip_image004

まとめ

このチュートリアルでは、キャッシュされたページのコンテンツを動的に更新する方法について説明しました。 HttpResponse.WriteSubstitution() メソッドを使用して、キャッシュされたページに動的コンテンツを挿入できるようにする方法について学習しました。 また、HTML ヘルパー メソッド内で WriteSubstitution() メソッドの呼び出しをカプセル化する方法についても学習しました。

可能な限りキャッシュを利用してください。Web アプリケーションのパフォーマンスに大きな影響を与えることができます。 このチュートリアルで説明したように、ページに動的コンテンツを表示する必要がある場合でも、キャッシュを利用できます。