2012-05-30 58 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. 页距离Tha缓存中检索和
  2. 正确的ASP.Net后送回ASP.Net调用remove()方法来我的页面
  3. 最后ASP.Net调用集()和页面更新 - 没有有效的缓存

    得到:查询A2/ss2.aspx(C:\ eShopKey \ ASP.Net \店\ myshoe_dev \缓存\ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)

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

    得到:A2/ss2.aspx(C:\ eShopKey \ ASP.Net \商店\ myshoe_dev \缓存\ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)检索和使用

    获取:查询A2/ss2.aspxHQFCNmycustom2VDE(C:\ eShopKey \ ASP.Net \商店\ myshoe_dev \缓存\ 3e72454ab3f36e4cfe3964e5063be622.txt)

    得到:A2/ss2.aspxHQFCNmycustom2VDE(C:\ eShopKey \ ASP.Net \商店\ myshoe_dev \缓存\ 3e72454ab3f36e4cfe3964e5063be622.txt)检索。

    得到:A2/ss2.aspxHQFCNmycustom2VDE(C:\ eShopKey \ ASP.Net \商店\ myshoe_dev \缓存\ 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 \缓存\ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)

    ADD:A2/ss2.aspx(C:\ eShopKey \ ASP。 Net \ Shops \ myshoe_dev \ Cache \ 7394fd15241e5b7f5c437ddf28dcd0e5.txt)已经存在。将被退回。

    Set:a2/ss2.aspxHQFCNmycustom2VDE(C:\ eShopKey \ ASP.Net \ Shops \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt)请求。

    Set: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
  • 另一个用于相同的页面但包含查询字符串参数

在我看来,第一个请求与第二个请求有某种关系,比如头部。

只要我连续调用相同的页面,使用相同的查询字符串,缓存按预期工作。

如果我接下来的页面:http://www.mydomain.com/mypage.aspx?param1=2

那么同样的,2步GET序列,被初始化。 ASP.Net发送2个GET请求,一个用于没有参数的页面和一个带有参数的页面。

然后在缓存中找到第一个GET请求(对于没有参数的页面)并返回到ASP.Net。但不知何故与第二个无关。它与调用的第一个变体(param1 = 1)有关。

因此,如果第二个请求之前被缓存,ASP.Net认为缓存页面无效并再次询问是否添加/设置。

总而言之,您似乎可以在给定时刻只有一个页面变化的缓存。之前所有的变化都将失效,因为页面将被再次调用其他参数。

由于ASP.NET使用相同的密钥来检索它,所以没有办法检查第一个GET请求与什么相关。

所以我的新问题:

  • 为什么ASP.Net发送2个请求每一页的自定义输出缓存提供?有人知道吗?
  • 我如何克服这种奇怪的行为?
  • AspNetInternalProvider是否具有相同的行为?

回答

0

第一次请求返回对象System.Web.Caching.CachedVary,第二次请求返回System.Web.Caching.OutputCacheEntry。根据对象名称,第一个为OutputCache,第二个为页面数据。

,如果您有任何疑问,请发送电子邮件至[email protected]

希望它可以帮助你!

阿米尔盛

+0

那么为了让OutputCacheProvider按预期工作,我必须采取哪些必要的操作?请注意,我在Internet上找到的每个自定义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

恭喜修复!如果可以,请确保将您的答案标记为“已接受”,以便其他人能够从您的成功中学习。干杯〜 –