I needed a quick way to cache some data for a feature I’m currently working on. The scenario is that the user should be able to type ahead and be presented with a list of matching options (AJAX auto complete). This data is coming from an external web service and is exposed via the core API of the application which in turn is exposed over WCF. So for AJAX to be able to get to this data it must talk to the WCF service, but this is not directly possible since cross site scripting is not allowed in the browser(at least not directly). So for AJAX to be able to consume this data I had to create a proxy WCF service implemented in the same project as the web application which in turn invokes the core WCF service to return this data.
Hence the proxy WCF service acts as an intermediary to fetch the data for auto complete. Now, the data is fairly static for the most part and might change every now and then. Moreover the data set is arguably large(approx 1000 items), so we now have 3 service layers (1 for the proxy WCF service, one for the core API WCF service and one for the external service which provides the actual data) to navigate through before the data is sent to the browser, clearly this is a performance bottleneck since I can’t afford to have this chatty behavior every single time a user types something in.
This leads us to caching the data, I could cache the data either in the core API object which invokes the remote service, OR in the core WCF service OR in the proxy WCF service.
Each has its own caveats. If I cache in the core API object or in the core WCF service I would need to either implement a custom caching mechanism or use an out of the box caching API such as the Enterprise Library Caching Application Block, I would also need to implement a expiration policy to ensure that the cache is flushed periodically, this would help provide some performance boost but there is still the overhead involved in marshalling this data to the client itself on every call.
If I cache at the client side (ASP.NET) then I can use the ASP.NET Cache object out of the box, and set up a time/file based dependency to periodically purge the cache, the downside is that when other clients invoke the core WCF service then the core API will invoke the external service every single time, and we could end up swamping both, the core WCF service and also the external service.
So, to address this we can cache at 2 levels; on the client side we can leverage ASP.NET Cache object and on the core API side we can use local caching.
The proxy WCF service has ASPNET compatibility mode turned on so that its invocable by Javascript/AJAX, this also allows us to tie into the ASP.NET execution pipeline and hence access the Cache object,
1: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
2: public class Service1 : IService1
3: {
4:
5: public string[] GetList(string prefixText, int count)
6: {
7: if (HttpContext.Current.Cache["items"] == null)
8: {
9: Debug.WriteLine("Adding Item to Cache");
10: var items = Enumerable.Range(1, 10).Select(i => i.ToString()).ToArray();
11: HttpContext.Current.Cache.Insert
12: ( "items",
13: items,
14: new CacheDependency(HttpContext.Current.Server.MapPath("Web.config")),
15: DateTime.UtcNow.AddMinutes(int.Parse(ConfigurationManager.AppSettings["CacheExpirationInMinutes"])),
16: Cache.NoSlidingExpiration
17: );
18: return items;
19: }
20: else
21: {
22: Debug.WriteLine("Read Item from Cache");
23: return HttpContext.Current.Cache["items"] as string[];
24:
25: }
26: }
27: }
Basically we’ve set up a dependency to changes in web.config and also to a time interval in minutes specified in web.config as an AppSetting, this means that if the time specified elapses or web.config changes, the cache is invalidated.
This does the trick for now, if I get a chance I’ll put in the caching mechanism in the core API as well. My primary choice is using the Caching application block, but that is a post for another day.
Happy coding.