Introduction
In this article, I’ll discuss caching in WCF services, and I’ll demonstrate how WCF caching can be implemented.
Source Code
The source code contains two Windows applications. The first hosts a demo WCF service. The second application is the WCF demo client. The solution also contains the WCFCache
class I wrote that allows you to easily add caching features to WCF services or any other type of application. For running the code, you’ll need the AdventureWorks database that can be downloaded here. Also, you’ll need to update the connection string in the configuration file of the WCF server with the required values.
WCF Caching
Caching in WCF, if used wisely, is definitely a good thing. Lets say that you have a method in your service that executes expensive queries, and this method is called very often, there is a big percentage of calls that pull the same data, the resulting data is not too big to be stored in memory (if, of course, it’s going to be stored in memory), and the data can be considered valid for a period of time, then this is the perfect place to use some sort of caching.
How can caching be added to WCF? You can implement a custom solution like this one, you can use some third party components, or you can use the System.Web.Caching.Cache
class. I don’t recommend the last one because I have never tried using it in this context. I found different opinions on the web about this. For me, the fact that Microsoft explicitly recommends not to do it was enough reason not to use it: “The Cache
class is not intended for use outside of ASP.NET applications. It was designed and tested for use in ASP.NET to provide caching for Web applications. In other types of applications, such as console applications or Windows Forms applications, ASP.NET caching might not work correctly.” http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx.
A typical data access method that uses caching executes the following two general steps: Checks the cache for the existence of the requested data and returns the data if found. If the data is not found in the cache, then it pulls it from the data store, stores it in the cache, and returns the data.
Also, you have to take care of removing or updating data in the cache under certain conditions. Usually, this is done when the data is no longer valid (was changed, or it wasn’t updated for too long), or the data was not used for too long.
Using the WCFCache class
The class was designed to provide similar functionality as System.Web.Caching.Cache
. The class is used through its static properties. It allows you to add or remove items from the cache. It has the possibility to indicate item expiration time and/or sliding expiration time. Below are some examples of using the class:
WCFCache.Current[productID] = product;
WCFCache.Current.Insert(productID, product, DateTime.Now.AddMinutes(2));
WCFCache.Current.Insert(productID, product, new TimeSpan(0, 0, 30), true);
product = (Product)WCFCache.Current[productID]
WCFCache.Current.CacheRefreshFrequency = new TimeSpan(0,0,10);
Now, let’s discuss a little on how it was implemented. Internally, the data is stored in a hash table for faster access. To make the class thread safe, I used the ReaderWriterLockSlim
class. ReaderWriterLockSlim
allows multiple threads for reading and exclusive access for writing. Below is how one of the overloaded versions of the Insert
method was implemented:
public void Insert(object key, object value)
{
_itemsLock.EnterWriteLock();
try
{
_items[key] = new WCFCacheItem(value);
}
finally
{
_itemsLock.ExitWriteLock();
}
}
where _itemsLock
is ReaderWriterLockSlim
and _items
is the hash table.
For inspecting the cache for expired items, I used System.Threading.Timer
.
Code Testing
For the purpose of testing the class, I created a WCF service and WCF client, both in the form of Windows applications. The client creates a number (it’s configurable from the UI) of threads on start. Each thread will pick a random product ID and try to get the data from the server on that product. On the server side, there are two methods exposed. One for getting the initial list of products IDs and another to get data on a particular product. Only the second method uses caching. Caching can be disabled or enabled in the server UI. Also, the sliding expiration time for the created cached items can be specified. Below are the results of running the test for 5 minutes with caching enabled:
and caching disabled:
As you can see, the average call time with cache enabled was 78 milliseconds, and with cache disabled was 528 milliseconds. If the query for getting the product would run longer, then the difference would be correspondingly bigger.
Conclusion
Thank you for reading. I hope this was useful to you.