Using Windows Server AppFabric Cache with WCF

When it comes to the need for speed nothing better than a Cache, much better if it’s from Microsoft and is part of Windows as it’s the case with Windows AppFabric Cache.

So how about implementing a cache for WCF?, well, BING will find a couple well written and well explain articles about the matter, the better one being this one:

http://blogical.se/blogs/mikael/archive/2010/09/12/use-appfabric-cache-to-cache-your-wcf-service-response.aspx?CommentPosted=true#commentmessage

At first the code doesn’t work because of the use of a timespan as an attribute, just remark that and it will compile and work great, however there is one little problem. The key for storing the objects in the cache is based only in the input parameters for the operation. What if you have two different operation with the same parameters? What if you have a couple operations with no parameters? Well, the code fails miserably because it will return a cached object that doesn’t correspond to the called operation.

How to fix this?, by using at least the operation name as part as the key, much better by using the namespace and the service name.

First, add a private variable to the CachingOperationInvoker to keep the operation name:

 1: public class CachingOperationInvoker : IOperationInvoker

2: {

 3:    IOperationInvoker \_innerOperationInvoker;

4: string _cacheName;

 5:    TimeSpan \_timeOut;

6: CacheHelper _cacheHelper;

 7:    string \_operationName;

8:  

 9: public CachingOperationInvoker(

10: IOperationInvoker innerOperationInvoker,

 11:    string cacheName, 

12: TimeSpan timeOut,

 13:    string operationName)

14: {

 15:    Trace.WriteLine("CachingOperationInvoker...");

16: _innerOperationInvoker = innerOperationInvoker;

 17:    \_cacheName = cacheName;

18: _timeOut = timeOut;

 19:    \_operationName = operationName;

20: _cacheHelper = new CacheHelper(_cacheName);

 21: }
```

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Populate that variable from the parameters in the call ApplyDispatchBehavior in the CacheOperationBehavior class

```
 1: public void ApplyDispatchBehavior(

2: OperationDescription operationDescription,

 3:    DispatchOperation dispatchOperation)

4: {

 5:    //Build the operation string using the namespace, 

6: //service name and operation name

 7:    //ie: http://thebestserviceever.com/myservice/myoperation

8: string operationDisambiguator =

 9:        operationDescription.DeclaringContract.Namespace +

10: “/” + operationDescription.DeclaringContract.Name + “/” +

 11:        operationDescription.Name;

12: // Replace the invoker with our own invoker

 13:    dispatchOperation.Invoker = 

14: new CachingOperationInvoker(dispatchOperation.Invoker,

 15:        \_cacheName, \_timeOut, operationDisambiguator);

16: }


.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

And include that variable in the Key by modifying the GetSerializedKey method:

1: private string GetSerializedKey(object[] inputs)

 2: {

3: StringBuilder sb = new StringBuilder();

 4:  

5: foreach (object o in inputs)

 6:    {

7: if (o.GetType().FullName ==

 8:        "System.ServiceModel.Channels.MessagePatterns+PatternMessage")

9: {

 10:            sb.Append(o.ToString() + "\\r\\n");

11: }

 12:        else

13: {

 14:            XmlSerializer serializer = new XmlSerializer(o.GetType());

15: MemoryStream stream = new MemoryStream();

 16:  

17: serializer.Serialize(stream, o);

 18:            string paramInput = 

19: Encoding.Default.GetString(stream.GetBuffer());

 20:            sb.Append(paramInput + "\\r\\n");

21: }

 22:    }

23: sb.Append(this._operationName);

 24:    MD5 md5Hasher = MD5.Create();

25: byte[] data =

 26:        md5Hasher.ComputeHash(

27: Encoding.Default.GetBytes(sb.ToString().Trim()));

 28:    Guid key = new Guid(data);

29: return key.ToString();

 30: }
```

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

That way the key will change for different operations and the cache will happily save the object and WCF performance will improve, specially with slow operations that just query the same data over and over again.

[![image](http://davidtriana.net/image.axd?picture=image_thumb_3.png "image")](http://davidtriana.net/image.axd?picture=image_3.png)

Another problem with this solution is that the behavior configuration is service wide. What if for some reason I need to apply the cache to a few operations only? what if I need to control that via configuration only?, well, if time permits that will be my next improvement to this code but that will be in another post.

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /\*white-space: pre;\*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }