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