2012-05-30 7 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(); 
     } 
    } 
} 

가 그때 난 내 내 Web.config의에 기본 출력 캐시 공급자를 변경 한 헤더

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

와 함께 .ASPX을 만들었습니다.

이상한 동작은 페이지가 캐시되지 않는다는 것입니다. 대신 이것은 디버깅 정보의 샘플 출력입니다.

  1. 페이지가 그쪽으로 캐시에서 검색 및
  2. 오른쪽이 ASP.Net 후 ASP.Net로 다시 전송 내 페이지
  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)가 이미 존재합니다. 반환됩니다.

    집합 : a2/ss2.aspxHQFCNmycustom2VDE (C : \ eShopKey \ ASP.Net \ Shops \ myshoe_dev \ Cache \ 3e72454ab3f36e4cfe3964e5063be622.txt)가 요청되었습니다.

    설정 : ss2.aspxHQFCNmycustom2VDE/A2 (C : \ eShopKey ASP.Net \ 상점 \ myshoe_dev \ 캐시 \ 3e72454ab3f36e4cfe3964e5063be622.txt가 \) : 30/05/2012 15시 7분 27초 디스크에 저장.그래서

내 질문 :

  1. ASP.Net 내 페이지를 무효화 유지 이유는 무엇입니까?
  2. Remove() 및 Set() 메서드가 ASP.Net에서 호출 될 때? 그것에 관한 정보를 찾지 못했습니다.
  3. 페이지의 이름을 바꾸고이 변형을 사용하면 캐싱이 작동합니다! 이것은 완전히 이상합니다.

기본 ASP.Net outputcacheprovider 캐싱을 사용하면 캐싱이 예상대로 작동합니다.


나는 계속하지만 문제를 해결 할 수없는 무엇을 발견 :

의 내가 페이지를 열 수 있다고 가정 해 봅시다 :

: http://www.mydomain.com/mypage.aspx?param1=1

ASP.Net 내 OutputCacheProvider 2 개 연속 GET 요청을 보냅니다

  • 하나에 대한 페이지 mypage.aspx
  • 동일한 페이지에 대해 다른 있지만 쿼리 문자열 매개 변수가 첨부 된

첫 번째 요청은 어떻게 든 헤더와 같이 두 번째 요청과 관련이있는 것으로 보입니다.

같은 쿼리 문자열을 사용하여 동일한 페이지를 연속적으로 호출하자마자 예상대로 캐시됩니다. 내가 페이지 옆에 호출하는 경우

: 다음 http://www.mydomain.com/mypage.aspx?param1=2

같은 2 단계 GET 순서는, 초기화됩니다. ASP.Net은 매개 변수가없는 페이지와 매개 변수가있는 페이지에 대해 각각 하나씩 두 개의 GET 요청을 보냅니다.

매개 변수가없는 페이지에 대한 첫 번째 GET 요청은 캐시에서 발견되어 ASP.Net으로 다시 반환됩니다. 그러나 어떻게 든 두 번째 것과 관련이 없습니다. 호출의 첫 번째 변형과 관련이 있습니다 (param1 = 1).

그럼에도 불구하고 두 번째 요청이 이전에 캐시 된 경우 ASP.Net은 캐시 된 페이지가 유효하지 않다고 생각하고 추가/설정을 다시 요청합니다.

요약하면 주어진 순간에 캐시에 대해 페이지의 변형을 한 개만 가질 수 있습니다. 페이지가 다른 매개 변수를 사용하여 다시 호출 될 때 이전의 모든 cahed 변형은 무효화됩니다.

ASP.NET은 동일한 키를 사용하여 검색하기 때문에 첫 번째 GET 요청과 관련이 있는지 확인할 방법이 없습니다.

그래서 내 새로운 질문 : ASP.Net 사용자 지정 출력 캐시 공급자에 모든 페이지에 대한이 개 요청을 보내는 이유는

  • ? 아무도 몰라?
  • 어떻게이 이상한 행동을 극복 할 수 있습니까?
  • AspNetInternalProvider의 동작이 동일합니까?

답변

0

첫 번째 요청은 System.Web.Caching.CachedVary 개체를 반환하고 두 번째 요청은 System.Web.Caching.OutputCacheEntry을 반환합니다. object의 이름에 따르면 첫 번째 것은 OutputCache이고 두 번째 것은 page의 데이터입니다.

당신은 질문이있는 경우에는, pls는

가 당신을 도울 수 있기를 바랍니다 [email protected] 이메일을 보내!

아미르 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

수정을 축하합니다! 가능한 경우 다른 사람들이 귀하의 성공을 통해 배울 수 있도록 귀하의 답변을 '수락'으로 표시하십시오. 건배 ~ –