My First Soap Extension
I've finally got a chance to write a Soap Extension. This one allows you to have caching for a web service method but the cache duration can be specified in the web.config file. The .NET "WebMethod" attribute supports caching but requires a constant value for the cache duration.
Here's the code for the extension:
You use the extension by adding the CacheExtension attribute to your
web service method:
Your web.config file should look something like this:
Some good links about Soap Extensions:
Here's the code for the extension:
1: using System;
2: using System.Configuration;
3: using System.Diagnostics;
4: using System.IO;
5: using System.Web;
6: using System.Web.Services.Protocols;
7: 8: namespace CacheExtensionProj
9: { 10: [AttributeUsage(AttributeTargets.Method)]11: public class CacheExtensionAttribute : SoapExtensionAttribute
12: {13: private string cacheDurationConfigKey;
14: private int priority;
15: 16: public override Type ExtensionType
17: {18: get { return typeof(CacheExtension); }
19: } 20: 21: public override int Priority
22: {23: get { return priority; }
24: set { priority = value; }
25: } 26: 27: public string CacheDurationConfigKey
28: {29: get { return cacheDurationConfigKey;}
30: set { cacheDurationConfigKey = value;}
31: } 32: } 33: 34: public class CacheExtension : SoapExtension
35: { 36: Stream originalStream; 37: Stream newStream;38: string cacheKey = null;
39: string cacheDurationConfigKey;
40: int cacheDuration = 0;
41: 42: public override object GetInitializer(LogicalMethodInfo methodInfo,
43: SoapExtensionAttribute attribute) 44: {45: //Cast attribute and return attribute value
46: CacheExtensionAttribute cea = (CacheExtensionAttribute)attribute;47: return cea.CacheDurationConfigKey;
48: } 49: 50: public override object GetInitializer(Type serviceType)
51: {52: return null;
53: } 54: 55: public override void Initialize(object initializer)
56: {57: cacheDurationConfigKey = (string)initializer;
58: if (cacheDurationConfigKey == null
59: cacheDurationConfigKey == string.Empty)
60: {61: cacheDurationConfigKey = "DefaultCacheDuration";
62: } 63: 64: cacheDuration = GetCacheDuration(cacheDurationConfigKey); 65: } 66: 67: public override void ProcessMessage(SoapMessage message)
68: {69: switch (message.Stage)
70: {71: case SoapMessageStage.BeforeSerialize:
72: break;
73: 74: case SoapMessageStage.AfterSerialize:
75: newStream.Position = 0; //need to reset stream
76: CopyStream(newStream, originalStream); 77: 78: SaveStreamToCache(cacheKey, newStream); 79: break;
80: 81: case SoapMessageStage.BeforeDeserialize:
82: //Cache key is the entire request xml string
83: cacheKey = GenerateCacheKeyFromStream(originalStream); 84: 85: Stream stream = GetStreamFromCache(cacheKey);86: if (stream != null)
87: {88: Trace.WriteLine("Returning Cached Version");
89: HttpContext.Current.Response.ContentType = 90: "text/xml";
91: CopyStream(stream, 92: HttpContext.Current.Response.OutputStream); 93: HttpContext.Current.Response.Flush(); 94: HttpContext.Current.Response.End(); 95: }96: else
97: { 98: CopyStream(originalStream, newStream);99: newStream.Position = 0; //need to reset stream
100: }101: break;
102: 103: case SoapMessageStage.AfterDeserialize:
104: break;
105: 106: default:
107: throw new Exception("invalid stage");
108: } 109: } 110: 111: public override Stream ChainStream( Stream stream )
112: { 113: originalStream = stream;114: newStream = new MemoryStream();
115: return newStream;
116: } 117: 118: private void CopyStream(Stream from, Stream to)
119: {120: TextReader reader = new StreamReader(from);
121: TextWriter writer = new StreamWriter(to);
122: writer.WriteLine(reader.ReadToEnd()); 123: writer.Flush(); 124: } 125: 126: private string GenerateCacheKeyFromStream(Stream stream)
127: {128: string cacheKey = new StreamReader(stream).ReadToEnd();
129: stream.Position = 0; //need to reset stream
130: return cacheKey;
131: } 132: 133: private void SaveStreamToCache(string cacheKey, Stream stream)
134: { 135: if (cacheDuration > 0)
136: {137: Trace.WriteLine("Saving result in Cache");
138: Trace.WriteLine("cacheKey = " + cacheKey);
139: 140: MemoryStream ms = new MemoryStream();
141: stream.Position = 0; 142: CopyStream(stream, ms); 143: ms.Position = 0; 144: 145: HttpContext.Current.Cache.Insert(cacheKey, ms, null,
146: DateTime.Now.AddHours((double)cacheDuration),
147: TimeSpan.Zero); 148: } 149: } 150: 151: private Stream GetStreamFromCache(string cacheKey)
152: {153: Stream stream = HttpContext.Current.Cache[cacheKey] as Stream;
154: if (stream != null)
155: { 156: stream.Position = 0; 157: }158: return stream;
159: } 160: 161: private int GetCacheDuration(string cacheDurationConfigKey)
162: {163: int cacheDurationValue = -1;
164: string cacheDurationStringValue =
165: ConfigurationSettings.AppSettings[cacheDurationConfigKey];166: if (cacheDurationStringValue != null &&
167: cacheDurationStringValue != string.Empty)
168: {169: try
170: {171: cacheDurationValue = int.Parse(cacheDurationStringValue);
172: if (cacheDurationValue <=0)
173: {174: Trace.WriteLine("Caching disabled. " +
175: cacheDurationConfigKey + 176: " must be positive and greater than zero.");
177: } 178: }179: catch(Exception e)
180: {181: Trace.WriteLine("Caching disabled. Unable to convert " +
182: cacheDurationConfigKey + 183: " value from configuration file into int type. \nException: "
184: + e.ToString()); 185: } 186: }187: else
188: {189: Trace.WriteLine("Caching disabled. " +
190: cacheDurationConfigKey + 191: " value is null or empty string.");
192: } 193: 194: return cacheDurationValue;
195: } 196: } 197: }You use the extension by adding the CacheExtension attribute to your
web service method:
1: [WebMethod]2: [CacheExtension(CacheDurationConfigKey="GetDateTime.CacheDuration")]
3: public string GetDateTime(string name)
4: {5: return "Hello " + name + " the time is " + DateTime.Now.ToString();
6: }Your web.config file should look something like this:
1: <appSettings>
2: <add key="DefaultCacheDuration" value="8"/>
3: <add key="GetDateTime.CacheDuration" value="2"/>
4: </appSettings>
Some good links about Soap Extensions:
1: http://msdn.microsoft.com/msdnmag/issues/04/03/ASPColumn/ 2: http://www.theserverside.net/tt/articles/showarticle.tss?id=SOAPExtensions 3: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspnet/html/asp03222001.asp 4: http://www.codeguru.com/columns/experts/article.php/c5479/ 5: http://www.mastercsharp.com/article.aspx?ArticleID=86&&TopicID=7