2012-05-30 2 views
0

У меня есть простой пользовательский OutputCacheProvider на основе файлов, основанный на образцах, которые я нашел в Интернете.ASP.Net 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 не требует установки() и страница обновляется - не эффективное кэширование

    Get: a2 Запросы/ss2.aspx (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)

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

    Get: a2/ss2.aspx (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 7394fd15241e5b7f5c437ddf28dcd0e5.txt) извлекаются и используются

    Get: a2/Запрос информации ss2.aspxHQFCNmycustom2VDE (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt)

    Get: a2/ss2.aspxHQFCNmycustom2VDE (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt) извлекается.

    Get: a2/ss2.aspxHQFCNmycustom2VDE (C: \ eShopKey \ ASP.Net \ Магазины \ 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) выполнено.

    ADD: a2/ss2.aspx (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)

    ADD: 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).

    Set: a2/ss2.aspxHQFCNmycustom2VDE (C: \ eShopKey \ ASP.Net \ Магазины \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt): 30/05/2012 15:07:27 сохраняется на диске.

Так что мои вопросы:

  1. Почему ASP.Net держит недействительности мою страницу?
  2. Когда методы Remove() и Set() вызывается ASP.Net? Я не нашел никакой информации об этом.
  3. Если я переименую страницу и использую этот вариант кеширования! Это совершенно странно.

Обратите внимание, что если я использую по умолчанию ASP.Net, то кэширование outputcacheprovider работает, как ожидалось.


Я нашел то, что происходит, но не в состоянии исправить:

Допустим, я открываю страницу: http://www.mydomain.com/mypage.aspx?param1=1

ASP.Net посылает 2 последовательных запросов GET к моему OutputCacheProvider:

  • один для страницы mypage.aspx
  • другой для той же страницы, но с параметрами querystring прилагается

Мне кажется, что первый запрос каким-то образом связан со вторым, как с заголовком.

Как только я вызываю последовательно одну и ту же страницу с тем же запросом, кеширование работает как ожидалось.

Если я предоставляю слово страницы: http://www.mydomain.com/mypage.aspx?param1=2

то же самое, 2 шаг GET последовательность инициализации. ASP.Net отправляет 2 запроса GET, один для страницы без параметров и один с параметрами.

Первый запрос GET (для страницы без параметров) затем находится в кеше и возвращается обратно в ASP.Net. Но почему-то не связано со вторым. Это связано с первой вариацией вызова (param1 = 1).

Итак, тем не менее, если второй запрос был кэширован раньше, ASP.Net считает, что кэшированная страница недействительна и снова запрашивает команду add/set.

Подводя итог, кажется, что вы можете иметь только один вариант страницы в кеше в данный момент. Все предыдущие варианты с сохранением недействительны, так как страница снова будет вызываться с другими параметрами.

Невозможно проверить, с чем связан первый запрос GET, поскольку ASP.NET использует тот же ключ для его получения.

Так мои новые вопросы:

  • Почему ASP.Net посылает 2 запросы на каждой странице поставщика кэша пользовательского вывода? Кто-нибудь знает?
  • Как я могу преодолеть это странное поведение?
  • Является ли AspNetInternalProvider одинаковым поведением?

ответ

0

Первый запрос возвращает объект System.Web.Caching.CachedVary, а второй запрос возвращает System.Web.Caching.OutputCacheEntry. В соответствии с именем объекта первый - для OutputCache, а второй - для данных страницы.

если у вас есть какие-либо вопросы, пожалуйста посылают электронную почту к [email protected]

Надеется, что это может помочь вам!

Amir Sheng

+0

Итак, каковы требуемые действия, которые я должен предпринять, чтобы OutputCacheProvider работал так, как ожидалось? Обратите внимание, что каждый отдельный пользовательский OutputCacheProvider, который я нашел в Интернете, страдает от одной и той же проблемы. – zissop

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 хэш-ключа). Посмотрите на реализацию моего первого опубликованного кода.

+0

Поздравляем с исправлением! Когда вы в состоянии, пожалуйста, отметьте свой ответ как «принятый», чтобы другие могли узнать о вашем успехе. Приветствия ~ –