さまざまな条件に応じて、任意のユーザー要素を一定時間キャッシュできます。 このようなキャッシュは非常に高速に動作し、ほとんどの場合これで十分ですが、私が参加しているプロジェクトではこれでは不十分であることが判明しました。
主な欠点は次のとおりです。
- キャッシュは、クエリ文字列のパラメーターだけでなく、Cookieやユーザーの種類などの他のパラメーターにも依存する場合があります(たとえば、オンラインストアでは、これは個人または法人です)
- プロジェクトの再コンパイル時にキャッシュ全体が消えてはいけません(これは通常、web.config構成ファイルを変更するときに起こります)
- 必要に応じて、キャッシュ全体をクリアできる必要があります
最初はサイト全体のキャッシュを定期的に生成していましたが、時間の経過とともにコントロールの複雑さのために非常に大きな問題になりましたので、別のソリューションを探すことにしました。
その本質は、それ自体をキャッシュする方法を知っているコントロールを作成することです。
これを行うには、UserControlクラスを取得し、それから派生したCacheUserControlクラスを作成します。
public abstract class CacheUserControl : UserControl
{
public abstract string GetCachePath();
public abstract TimeSpan GetCacheTime();
public bool IsCache { get ; protected set ; }
protected string cacheText;
protected string cachePath;
protected sealed override void OnInit( EventArgs e)
{
...
}
public sealed override void RenderControl(HtmlTextWriter htmlTextWriter)
{
...
}
}
ご覧のとおり、2つの抽象メソッドがあります。
GetCachePathメソッドは、キャッシュファイルへのパスを返します。 このパスは、正しいファイルを選択できるように、コントロールのすべての依存関係を反映する必要があります。
GetCacheTimeメソッドは、キャッシュが廃止されたと見なされ、必要に応じて更新されるまでの時間間隔を返します。
次に、UserControlクラスの2つのメソッドをオーバーライドする必要があります。
OnInitメソッドは、キャッシュのあるファイルが存在するかどうかを確認する必要があります。存在する場合、コントロールはすべての子コントロールを削除し、イベントが呼び出されないようにし、ファイルからキャッシュを読み込みます。
protected sealed override void OnInit( EventArgs e)
{
try
{
cachePath = GetCachePath();
if (cachePath != null )
{
var cacheFile = new FileInfo(cachePath);
if (cacheFile.Exists)
{
var cacheTime = GetCacheTime();
var cacheTimeExpired = cacheFile.CreationTime + cacheTime < DateTime .Now;
if (!cacheTimeExpired)
{
using ( var reader = cacheFile.OpenText())
{
cacheText = reader.ReadToEnd();
IsCache = true ;
Controls.Clear();
}
}
}
}
}
catch (IOException)
{
}
base .OnInit(e);
}
RenderControlメソッドは、キャッシュがロードされているかどうかをチェックし、ロードされている場合はレンダリングし、ロードされていない場合はコントロールを通常モードでレンダリングし、さらにキャッシュファイルを作成する必要があります。
public sealed override void RenderControl(HtmlTextWriter htmlTextWriter)
{
if (IsCache)
{
htmlTextWriter.Write(cacheText);
}
else
{
base .RenderControl(htmlTextWriter);
try
{
if (cachePath != null )
{
using ( var streamWriter = new StreamWriter(cachePath, false , Encoding .UTF8))
{
using (htmlTextWriter = new HtmlTextWriter(streamWriter))
{
base .RenderControl(htmlTextWriter);
}
}
}
}
catch (IOException)
{
}
}
}
その結果、自己キャッシュコントロールを使用するには、基本クラスをUserControlからCacheUserConrtolに変更し、さらに次のように2つのメソッドを定義する必要があります。
public override string GetCachePath()
{
var id = Request.QueryString[ "id" ];
if ( string .IsNullOrEmpty(id)) return null ;
var currency = "rur" ;
var cookie = Request.Cookies[ "currency" ];
if (cookie != null )
{
var value = cookie.Value;
if ( value == "usd" ) currency = "usd" ;
else if ( value == "euro" ) currency = "euro" ;
}
var path = string .Format( "~/cache/cat_{0}_cur_{1}.html" , id, currency);
return Server.MapPath(path);
}
public override TimeSpan GetCacheTime()
{
return TimeSpan .FromDays(7);
}
これで、コントロール自体が各ディレクトリ通貨(Cookieから決定)のさまざまなカテゴリ(IDによって決定)に個別のキャッシュファイルを作成します
このアプローチの唯一の欠点は、時間の経過とともにガベージがキャッシュに蓄積されるため、定期的に完全にクリーニングする必要があることです。 また、このようなコントロールのPage_Loadメソッドはどのような場合でも呼び出されますが、子コントロールのイベントはコンテンツがファイルから取得されていない場合にのみ呼び出されることにも注意してください。
まあ、それがすべてです。