我根据在互联网上找到的样本实现了一个简单的基于文件的自定义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();
}
}
}
然后我创建了带标题<。p>的.aspx
<%@ OutputCache Duration="3600" Location="Server" VaryByParam="*" %>
我已将默认输出缓存提供程序更改为我的web.config以进行挖掘。
奇怪的行为是页面没有被缓存。相反,这是我的调试信息的示例输出。看来:
最后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)检索。
获取: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)检索。
获取: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)。
ADD:a2 / ss2.aspx(C:\ eShopKey \ ASP.Net \ Shops \ 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)。
设置:a2 / ss2.aspxHQFCNmycustom2VDE(C:\ eShopKey \ ASP.Net \ Shops \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt):30/05/2012 15:07:27保存到磁盘。
< / LI> 醇>所以我的问题:
请注意,如果我使用默认的ASP.Net outputcacheprovider缓存按预期工作。
我发现了正在发生但无法解决的问题:
假设我打开页面:http://www.mydomain.com/mypage.aspx?param1=1
ASP.Net向OutputCacheProvider发送2个连续的GET请求:
在我看来,第一个请求与第二个请求有某种关系,就像标题一样。
一旦我连续调用同一个页面,使用相同的查询字符串,缓存就会按预期工作。
如果我在下一页打电话:http://www.mydomain.com/mypage.aspx?param1=2
然后初始化相同的2步GET序列。 ASP.Net发送2个GET请求,一个用于没有参数的页面,另一个用于参数。
然后在缓存中找到第一个GET请求(对于没有参数的页面)并返回到ASP.Net。但不知何故与第二个无关。它与呼叫的第一个变体(param1 = 1)有关。
因此,如果第二个请求之前已被缓存,ASP.Net认为缓存页面无效并再次询问添加/设置。
总结一下,似乎您可以在给定时刻只将页面的一个变体添加到缓存中。由于将使用其他参数再次调用页面,因此之前的所有曲线变体都将失效。
由于ASP.NET使用相同的密钥来检索它,因此无法检查第一个GET请求的相关内容。
所以我的新问题:
答案 0 :(得分: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哈希值)。在我的第一个发布的代码中查找实现。
答案 1 :(得分:0)
第一个请求返回一个对象System.Web.Caching.CachedVary
,第二个请求返回System.Web.Caching.OutputCacheEntry
。根据对象的名称,第一个用于OutputCache
,第二个用于页面数据。
如果您有任何疑问,请发送电子邮件至shengzhengshan@hotmail.com
希望它可以帮到你!
Amir Sheng