Silverlight and WCF caching
- by subodhnpushpak
There are scenarios where Silverlight client calls WCF (or REST) service for data. Now, if the data is cached on the WCF layer, the calls can take considerable resources at the server if NOT cached. Keeping that in mind along with the fact that cache is an cross-cutting aspect, and therefore it should be as easy as possible to put Cache wherever required. The good thing about the solution is that it caches based on the inputs. The input can be basic type of any complex type. If input changes the data is fetched and then cached for further used. If same input is provided again, data id fetched from the cache. The cache logic itself is implemented as PostSharp aspect, and it is as easy as putting an attribute over service call to switch on cache. Notice how clean the code is: [OperationContract] [CacheOnArgs(typeof(int))] // based on actual value of cache public string DoWork(int value) { return string.Format("You entered: {0} @ cached time {1}", value, System.DateTime.Now); } The cache is implemented as POST Sharp as below 1: public override void OnInvocation(MethodInvocationEventArgs eventArgs)
2: {
3: try
4: {
5: object value = new object();
6: object[] args = eventArgs.GetArgumentArray();
7: if (args != null || args.Count() > 0)
8: {
9:
10: string key = string.Format("{0}_{1}", eventArgs.Method.Name, XMLUtility<object>.GetDataContractXml(args[0], null));// Compute the cache key (details omitted).
11:
12:
13: value = GetFromCache(key);
14: if (value == null)
15: {
16: eventArgs.Proceed();
17: value = XMLUtility<object>.GetDataContractXml(eventArgs.ReturnValue, null);
18: value = eventArgs.ReturnValue;
19: AddToCache(key, value);
20: return;
21: }
22:
23:
24: Log(string.Format("Data returned from Cache {0}",value));
25: eventArgs.ReturnValue = value;
26: }
27: }
28: catch (Exception ex)
29: {
30: //ApplicationLogger.LogException(ex.Message, Source.UtilityService);
31: }
32: }
33:
34: private object GetFromCache(string inputKey) { if (ServerConfig.CachingEnabled) { return WCFCache.Current[inputKey]; } return null; }private void AddToCache(string inputKey,object outputValue)
35: {
36: if (ServerConfig.CachingEnabled)
37: {
38: if (WCFCache.Current.CachedItemsNumber < ServerConfig.NumberOfCachedItems)
39: {
40: if (ServerConfig.SlidingExpirationTime <= 0 || ServerConfig.SlidingExpirationTime == int.MaxValue)
41: {
42: WCFCache.Current[inputKey] = outputValue;
43: }
44: else
45: {
46: WCFCache.Current.Insert(inputKey, outputValue, new TimeSpan(0, 0, ServerConfig.SlidingExpirationTime), true);
47:
48: // _bw.DoWork += bw_DoWork;
49: //string arg = string.Format("{0}|{1}", inputKey,outputValue);
50: //_bw.RunWorkerAsync(inputKey );
51: }
52: }
53: }
54: }
The cache class can be extended to support Velocity / memcahe / Nache.
the attribute can be used over REST services as well.
Hope the above helps. Here is the code base for the same.
Please do provide your inputs / comments.