0

インターネットで見つけたサンプルに基づいて、単純なファイルベースのカスタム OutputCacheProvider を実装しました。

コードは次のとおりです。

using System;
using System.Configuration;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Xml.Serialization;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;


namespace SimpleCachedProvider
{
    public class FileCacheProvider : OutputCacheProvider {
        private string _cachePath;

        void WriteToFile(String filename, String contents) {
            FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
            StreamWriter w = new StreamWriter(fs, System.Text.Encoding.GetEncoding(1253));

            w.BaseStream.Seek(0, SeekOrigin.Begin);
            w.BaseStream.SetLength(0);
            w.Write(contents);
            w.Flush();
            w.Close();
        }

        void AppendToFile(String filename, String contents) {
            if (contents.ToLower().IndexOf("ss2.aspx") >= 0 || contents.ToLower().IndexOf("default.aspx") >= 0) {
                FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
                StreamWriter w = new StreamWriter(fs, System.Text.Encoding.GetEncoding(1253));

                w.BaseStream.Seek(0, SeekOrigin.End);
                w.Write(contents);
                w.Flush();
                w.Close();
            }
        }

        private string CachePath {
            get {
                if (!string.IsNullOrEmpty(_cachePath))
                    return _cachePath;

                _cachePath = ConfigurationManager.AppSettings["OutputCachePath"];
                var context = HttpContext.Current;

                if (context != null) {
                    _cachePath = context.Server.MapPath(_cachePath);
                    if (!_cachePath.EndsWith("\\"))
                        _cachePath += "\\";
                }

                return _cachePath;
            }
        }

        public override object Add(string key, object entry, DateTime utcExpiry) {
            var path = GetPathFromKey(key);

            AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ")\r\n");

            if (File.Exists(path)) {
                AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") already exists. Will be returned.\r\n");
                return entry;
            }

            AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") does not exists. Will be created.\r\n");
            using (var file = File.OpenWrite(path)) {
                var item = new CacheItem { Expires = utcExpiry, Item = entry };
                var formatter = new BinaryFormatter();
                formatter.Serialize(file, item);
                AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") saved to disk.\r\n");
            }

            return entry;
        }

        public override void Set(string key, object entry, DateTime utcExpiry) {
            var item = new CacheItem { Expires = utcExpiry, Item = entry };
            var path = GetPathFromKey(key);

            AppendToFile(CachePath + "info.txt", "Set: " + key + " (" + path + ") requested.\r\n");

            using (var file = File.OpenWrite(path)) {
                var formatter = new BinaryFormatter();
                formatter.Serialize(file, item);
                AppendToFile(CachePath + "info.txt", "Set: " + key + " (" + path + "): " + utcExpiry.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss") + " saved to disk.\r\n");
            }
        }

        public override object Get(string key) {
            var path = GetPathFromKey(key);

            AppendToFile(CachePath + "info.txt", "Get: Querying " + key + " (" + path + ")\r\n");

            if (!File.Exists(path)) {
                AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") not found.\r\n");
                return null;
            }

            CacheItem item = null;

            using (var file = File.OpenRead(path)) {
                var formatter = new BinaryFormatter();
                item = (CacheItem)formatter.Deserialize(file);
                AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") retrieved.\r\n");
            }

            if (item == null || item.Expires <= DateTime.Now.ToUniversalTime()) {
                AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") deleted due to expiration.\r\n");
                Remove(key);
                return null;
            }

            AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") retrieved and used\r\n");

            return item.Item;
        }

        public override void Remove(string key) {
            var path = GetPathFromKey(key);

            AppendToFile(CachePath + "info.txt", "Remove: " + key + " (" + path + ") requested.\r\n");

            if (File.Exists(path)) {
                AppendToFile(CachePath + "info.txt", "Remove: " + key + " (" + path + ") executed.\r\n");
                File.Delete(path);
            }
        }

        private string GetPathFromKey(string key) {
            return CachePath + MD5(key) + ".txt";
        }

        private string MD5(string s) {
            MD5CryptoServiceProvider provider;
            provider = new MD5CryptoServiceProvider();
            byte[] bytes = Encoding.UTF8.GetBytes(s);
            StringBuilder builder = new StringBuilder();

            bytes = provider.ComputeHash(bytes);

            foreach (byte b in bytes)
                builder.Append(b.ToString("x2").ToLower());

            return builder.ToString();
        }
    }
}

次に、ヘッダーを含む .aspx を作成しました

<%@ OutputCache Duration="3600" Location="Server" VaryByParam="*" %>

デフォルトの出力キャッシュ プロバイダーを自分の web.config に変更しました。

奇妙な動作は、ページがキャッシュされていないことです。代わりに、これは私のデバッグ情報からの出力例です。のようだ:

  1. ページはキャッシュから取得され、ASP.Net に送り返されます。
  2. その直後、ASP.Net は私のページに Remove() メソッドを呼び出します
  3. 最後に ASP.Net が Set() を呼び出し、ページが更新されます - 効果的なキャッシュはありません

    取得: a2/ss2.aspx のクエリ (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt)

    取得: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) を取得しました。

    Get: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) 取得して使用

    取得: a2/ss2.aspxHQFCNmycustom2VDE のクエリ (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt)

    取得: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) を取得しました。

    Get: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) 取得して使用

    削除: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) が要求されました。

    削除: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) が実行されました。

    追加: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt)

    追加: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) は既に存在します。返されます。

    セット: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) が要求されました。

    セット: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt): 30/05/2012 15:07:27 がディスクに保存されました。

だから私の質問:

  1. ASP.Net がページを無効にし続けるのはなぜですか?
  2. ASP.Net によって Remove() および Set() メソッドが呼び出されるのはいつですか? それに関する情報は見つかりませんでした。
  3. ページの名前を変更してこのバリエーションを使用すると、キャッシュが機能します! これはまったく奇妙です。

デフォルトの ASP.Net outputcacheprovider を使用すると、キャッシングが期待どおりに機能することに注意してください。


何が起こっているのかを見つけましたが、修正できませんでした:

ページを開いたとしましょう: http://www.mydomain.com/mypage.aspx?param1=1

ASP.Net は 2 つの連続した GET 要求を OutputCacheProvider に送信します。

  • ページ mypage.aspx 用の 1 つ
  • 同じページの別のものですが、クエリ文字列パラメーターが添付されています

ヘッダーのように、最初のリクエストが 2 番目のリクエストと何らかの形で関連しているように思えます。

同じクエリ文字列を使用して同じページを連続して呼び出すとすぐに、キャッシングが期待どおりに機能します。

次のページを呼び出す場合: http://www.mydomain.com/mypage.aspx?param1=2

次に、同じ 2 ステップの GET シーケンスが初期化されます。ASP.Net は 2 つの GET 要求を送信します。1 つはパラメーターなしのページ用で、もう 1 つはパラメーター付きです。

次に、最初の GET 要求 (パラメーターのないページに対する) がキャッシュで検出され、ASP.Net に返されます。しかし、どういうわけか2番目のものとは無関係です。これは、呼び出しの最初のバリエーション (param1=1) に関連しています。

それにもかかわらず、2 番目の要求が以前にキャッシュされていた場合、ASP.Net はキャッシュされたページが無効であると判断し、追加/設定を再度要求します。

要約すると、特定の瞬間にページのバリエーションを 1 つだけキャッシュに入れることができるようです。ページが他のパラメーターで再度呼び出されるため、以前にキャッシュされたすべてのバリエーションは無効になります。

ASP.NET は同じキーを使用して取得するため、最初の GET 要求が何に関連しているかを確認する方法はありません。

だから私の新しい質問:

  • ASP.Net がページごとに 2 つの要求をカスタム出力キャッシュ プロバイダーに送信するのはなぜですか? 誰か知っていますか?
  • この奇妙な動作をどのように克服できますか?
  • AspNetInternalProvider の動作は同じですか?
4

2 に答える 2

1

解決策を見つけました!問題は Add メソッドにありました。以下のように、すべてのプロバイダーに記述する必要があります。

public override object Add(string key, object entry, DateTime utcExpiry) {
        String vKey = TransformKey(key);

        object res = Get(key);
        if (res == null) {
            Set(key, entry, utcExpiry);
            return entry;
        }

        return res;
    }

TransformKey メソッドは、キー (キーの MD5 ハッシュなど) に基づいて安全な文字列 (不正な文字を含まない文字列) を返すだけです。最初に投稿したコードの実装を探してください。

于 2012-06-01T22:26:09.587 に答える
0

最初のリクエストはオブジェクトSystem.Web.Caching.CachedVaryを返し、2番目のリクエストはを返しますSystem.Web.Caching.OutputCacheEntry。オブジェクトの名前によると、最初のオブジェクトは、用OutputCacheで、2番目のオブジェクトはページのデータ用です。

ご不明な点がございましたら、plsはshengzhengshan@hotmail.comにメールを送信してください

それがあなたを助けることができることを願っています!

アミールシェン

于 2012-06-01T07:53:08.950 に答える