Introduction

Some months ago I created a custom WCF protocol channel to transparently inject client-side caching capabilities into an existing WCF-enabled application just changing the its configuration file. Since I posted my first post on this subject I received positive feedbacks on the client-side caching pattern, hence I decided to create a version of my component for Windows Azure. The new implementation is almost identical to the original version and introduces some minor changes. The most relevant addition is the ability to cache response messages in  Windows Azure AppFabric Caching Service. The latter enables to easily create a cache in the cloud that can be used by Windows Azure roles to store reference and lookup data. Caching can dramatically increase performance by temporarily storing information from backend stores and sources. My component allows to boost the performance of existing WCF-enabled applications running in the Azure environment by injecting caching capabilities in the WCF channel stack that transparently exploit the functionality offered by Windows Azure AppFabric Caching to avoid redundant calls against remote WCF and ASMX services. The new version of the WCF custom channel provides the possibility to choose among three caching providers:

  • A Memory Cache-based provider: this component internally uses an instance of the MemoryCache class contained in the .Net Framework 4.0.
  • A Web Cache-based provider: this provider utilizes an instance of the Cache class supplied by ASP.NET to cache response messages.
  • An AppFabric Caching provider: this caching provider leverages Windows Azure AppFabric Caching Service. To further improve the performance, it’s highly recommended the client application to use the Local Cache to store response messages in-process.

As already noted in the previous article, client-side  caching and server-side caching are two powerful and complimentary techniques to improve the performance of WCF-enabled applications.  Client-caching is particularly indicated for those applications, like a web site, that frequently invoke one or multiple back-end systems to retrieve reference and lookup data, that is data that is typically static, like the catalog of an online store and that changes quite infrequently. By using client-side caching you can avoid making redundant calls to retrieve the same data over time, especially when these calls take a significant amount of time to complete.

For more information on Windows Azure AppFabric, you can review the following articles:

Problem Statement

The problem statement that the cloud version of my component intends to solve can be formulated as follows:

  • How can I implicitly cache response messages within a service or an application running in a Windows Azure role that invokes one or multiple underlying services using WCF and a Request-Response message exchange pattern without modifying the code of the application in question?

To solve this problem, I created a custom protocol channel that you can explicitly or implicitly use inside a CustomBinding when specifying client endpoints within the configuration file or by code using the WCF API.

Design Pattern

The design pattern implemented by my component can be described as follows: the caching channel allows to extend existing WCF-enabled cloud applications and services with client-caching capabilities without the need to explicitly change their code to exploit the functionality supplied by Windows Azure AppFabric Caching. To achieve this goal, I created a class library that contains a set of components to configure, instantiate and use this caching channel at runtime. My custom channel is configured to checks the presence of the response message in the cache and behaves accordingly:

  • If the response message is in the cache, the custom channel immediately returns the response message from the cache without invoking the underlying service.
  • Conversely, if the response message is not in the cache, the custom channel calls the underlying channel to invoke the back-end service and then caches the response message using the caching provider defined in the configuration file for the actual call.

You can exploit the capabilities of the caching channel in 2 distinct ways:

  • You can configure the client application to use a CustomBinding. In this case, you have to specify the ClientCacheBindingElement as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
  • Alternatively, you can use the ClientCacheEndpointBehavior to inject the ClientCacheBindingElement at the top of an existing binding, for example the BasicHttpBinding or the WsHttpBinding.

Scenarios

This section describes 2 scenarios where you can use the caching channel in a cloud application.

First Scenario

The following picture depicts the architecture of the first scenario that uses Windows Azure Connect to establish a protected connection between a web role in the cloud and local WCF service and uses Windows Azure AppFabric Caching provider to cache response messages in the local and distributed cache. The diagram below shows an ASP.NET application running in a Web Role that invokes a local, on-premises WCF service running in a corporate data center. In particular, when the Service.aspx page first loads, it populates a drop-down list with the name of writers from the Authors table of the notorious pubs database which runs on-premise in the corporate domain. The page offers the user the possibility to retrieve the list of books written by the author selected in the drop-down list by invoking the AuthorsWebService WCF service located in the organization’s network that retrieves data for from the Titles tables of the pubs database.

  1. Directly invoke the WCF service via Windows Azure Connect: the Service.aspx page always calls the AuthorsWebService to retrieve the list of titles for the current author using Windows Azure Connect (see below for more information).
  2. Use Caching API Explicitly and via Windows Azure Connect: the page explicitly uses the cache aside programming pattern and the Windows Azure AppFabric Caching API to retrieve  the list of titles for the current author. When the user presses the Get Titles button, the page first checks whether data is in the cache: if yes it retrieves titles from the cache, otherwise it invokes the underlying WCF service, writes the data in the distributed cache, then returns the titles.
  3. Use Caching API via caching channel and Windows Azure Connect: this mode uses the caching channel to transparently retrieve data from the local or distributed cache. The use of the caching channel allows to transparently inject client-side caching capabilities into an existing WCF-enabled application without the need to change its code. In this case, it’s sufficient to change the configuration of the client endpoint in the web.config.
  4. Directly invoke the WCF service via Service Bus: the Service.aspx page always calls the AuthorsWebService to retrieve the list of titles for the current author via Windows Azure AppFabric Service Bus. In particular, the web role invokes a BasicHttpRelayBinding endpoint exposed in the cloud via relay service by the AuthorsWebService.
  5. Use Caching API via caching channel and Service Bus: this mode uses the Service Bus to invoke the AuthorsWebService and the caching channel to transparently retrieve data from the local or distributed cache. The ClientCacheEndpointBehavior replaces the original BasicHttpRelayBinding specified in the configuration file with a CustomBinding that contains the same binding elements and injects the ClientCacheBindingElement at the top of the binding. This way, at runtime, the caching channel will be the first channel to be invoked in the channel stack.

WindowsAzureConnect

Let’s analyze what happens when the user selects the third option to use the caching channel with Windows Azure Connect.

Message Flow:

  1. The user chooses an author from the drop-down list, selects the Use Caching API via WCF channel call mode and finally presses the Get Titles button.
  2. This event triggers the execution of the GetTitles method that creates a WCF proxy using the UseWCFCachingChannel endpoint. This endpoint is configured in the web.config to use the CustomBinding. The ClientCacheBindingElement is defined as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
  3. The proxy transforms the .NET method call into a WCF message and delivers it to the underlying channel stack.
  4. The caching channel checks whether the response message in the local or distributed cache. If ASP.NET application is hosted by more than one web role instance, the response message may have been previously put in the distributed cache by another role instance. If the caching channel finds the response message for the actual call in the local or distributed cache, it immediately returns this message to the proxy object without invoking the back-end service.
  5. Conversely, if the response message is not in the cache, the custom channel calls the inner channel to invoke the back-end service. In this case, the request message goes all the way through the channel stack to the transport channel that invokes the AuthorsWebService.
  6. The AuthorsWebService uses the authorId parameter to retrieve a list of books from the Titles table in the pubs database.
  7. The service reads the titles for the current author from the pubs database.
  8. The service returns a response message to the ASP.NET application.
  9. The transport channel receives the stream of data and uses a message encoder to interpret the bytes and to produce a WCF Message object that can continue up the channel stack. At this point each protocol channel has a chance to work on the message. In particular, the caching channel stores the response message in the distributed cache using the AppFabricCaching provider.
  10. The caching channel returns the response WCF message to the proxy.
  11. The proxy transforms the WCF message into a response object.
  12. The ASP.NET application creates and returns a new page to the browser.

Second Scenario

The following picture depicts the architecture of the second scenario where the web role uses the Windows Azure AppFabric Service Bus to invoke the AuthorsWebService and Windows Azure AppFabric Caching provider to cache response messages in the local and distributed cache.

ServiceBus

Let’s analyze what happens when the user selects the fifth option to use the caching channel with the Service Bus.

Message Flow:

  1. The user chooses an author from the drop-down list, selects the Use Caching API via WCF channel call mode and finally presses the Get Titles button.
  2. This event triggers the execution of the GetTitles method that creates a WCF proxy using the UseWCFCachingChannel endpoint. This endpoint is configured in the web.config to use the CustomBinding. The ClientCacheBindingElement is defined as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
  3. The proxy transforms the .NET method call into a WCF message and delivers it to the underlying channel stack.
  4. The caching channel checks whether the response message in the local or distributed cache. If ASP.NET application is hosted by more than one web role instance, the response message may have been previously put in the distributed cache by another role instance. If the caching channel finds the response message for the actual call in the local or distributed cache, it immediately returns this message to the proxy object without invoking the back-end service.
  5. Conversely, if the response message is not in the cache, the custom channel calls the inner channel to invoke the back-end service via the Service Bus. In this case, the request message goes all the way through the channel stack to the transport channel that invokes the relay service.
  6. The Service Bus relays the request message to the AuthorsWebService.
  7. The AuthorsWebService uses the authorId parameter to retrieve a list of books from the Titles table in the pubs database.
  8. The service reads the titles for the current author from the pubs database.
  9. The service returns a response message to the relay service.
  10. The relay service passes the response message to the ASP.NET application.
  11. The transport channel receives the stream of data and uses a message encoder to interpret the bytes and to produce a WCF Message object that can continue up the channel stack. At this point each protocol channel has a chance to work on the message. In particular, the caching channel stores the response message in the distributed cache using the AppFabricCaching provider.
  12. The caching channel returns the response WCF message to the proxy.
  13. The proxy transforms the WCF message into a response object.
  14. The ASP.NET application creates and returns a new page to the browser.

Quotes_Icon NOTE
In the context of a cloud application, the use of the caching channel not only improves performance, but allows to decrease the traffic on Windows Azure Connect and Service Bus and therefore the cost due to operations performed and network used.

Windows Azure Connect

In order to establish an IPsec protected IPv6 connection between the Web Role running in the Windows Azure data center and the local WCF service running in the organization’s network, the solution exploits Windows Azure Connect that is main component of the networking functionality that will be offered under the Windows Azure Virtual Network name. Windows Azure Connect enables customers of the Windows Azure platform to easily build and deploy a new class of hybrid, distributed applications that span the cloud and on-premises environments. From a functionality standpoint, Windows Azure Connect provides a network-level bridge between applications and services running in the cloud and on-premises data centers. Windows Azure Connect makes it easier for an organization to migrate their existing applications to the cloud by enabling direct IP-based network connectivity with their existing on-premises infrastructure. For example, a company can build and deploy a hybrid solution where a Windows Azure application connects to an on-premises SQL Server database, a local file server or an LOB applications running the corporate network.

For more information on Windows Azure Connect, you can review the following resources:

Windows Azure AppFabric Service Bus

The Windows Server AppFabric Service Bus is an Internet-scale Service Bus that offers scalable and highly available connection points for application communication. This technology allows to create a new range of hybrid and distributed applications that span the cloud and corporate environments. The AppFabric Service Bus is designed to provide connectivity, queuing, and routing capabilities not only for the cloud applications but also for on-premises applications. The Service Bus and in particular Relay Services support the WCF programming model and provide a rich set of bindings to cover a complete spectrum of design patterns:

  • One-way communications
  • Publish/Subscribe messaging
  • Peer-to-peer communications
  • Multicast messaging
  • Direct connections between clients and services

The Relay Service is a service residing in the cloud, whose job is to assist in the connectivity, relaying the client calls to the service. Both the client and service can indifferently reside on-premises or in the cloud.

For more information on the Service Bus, you can review the following resources:

We are now ready to delve into the code.

Solution

The solution code has been implemented in C# using Visual Studio 2010 and the .NET Framework 4.0. The following picture shows the projects that comprise the WCFClientCachingChannel solution.

CloudSolution

A brief description of the individual projects is indicated below:

  • AppFabricCache: this caching provider implements the Get and Put methods to retrieve and store data items from\to Windows Azure AppFabric Caching.
  • MemoryCache: this caching provider provides the Get and Put methods to retrieve and store items to a static in-process MemoryCache object.
  • WebCache: this caching provider provides the Get and Put methods to retrieve and store items to a static in-process Web Cache object.
  • ExtensionLibrary: this assembly contains the WCF extensions to configure, create and run the caching channel at runtime.
  • Helpers: this library contains the helper components used by the WCF extensions objects to handle exceptions and trace messages.
  • Pubs WS: this project contains the code for the AuthorsWebService.
  • Pubs: this test project contains the definition and configuration for the Pubs web role.
  • PubsWebRole: this project contains the code of the ASP.NET application running in Windows Azure.

As I mentioned in the introduction of this article, the 3 caching providers have been modified to be used in a cloud application. In particular, the AppFabricCache project has been modified to use the Windows Azure AppFabric Caching API in place of their on-premises counterpart. Windows Azure AppFabric uses the same cache client programming model as the on-premise solution of Windows Server AppFabric. However, the 2 API are not identical and there are relevant differences when developing a Windows Azure AppFabric Caching solution compared to developing an application that leverages Windows Server AppFabric Caching . For more information on this topic, you can review the following articles:

Quotes_Icon NOTE
The Client API of Windows Server AppFabric Caching and Windows Azure AppFabric Caching have the same fully-qualified name. So, what happens when you install the Windows Azure AppFabric SDK on a development machine where Windows Server AppFabric Caching is installed? The setup process of Windows Server AppFabric installs the Cache Client API assemblies in the GAC whereas the Windows Azure AppFabric SDK copies the assemblies in the installation folder, but it doesn’t register them in the GAC. Therefore, if you create an Azure application on a development machine hosting both the on-premises and cloud version of the Cache Client API, even if you reference the Azure version in your web or worker role project, when you debug the application within the Windows Azure Compute Emulator, your role will load the on-premises version, that is, the wrong version of the Cache Client API. Fortunately, the 2 on-premises and cloud versions of the API have the same fully-qualified name but different version number, hence you can include the following snippet in the web.config configuration file of your role to refer the right version of the API.

<!-- Assembly Redirection -->
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.ApplicationServer.Caching.Client"
                        publicKeyToken="31bf3856ad364e35"
                        culture="Neutral" />
      <bindingRedirect oldVersion="1.0.0.0"
                        newVersion="101.0.0.0"/>
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.ApplicationServer.Caching.Core"
                        publicKeyToken="31bf3856ad364e35"
                        culture="Neutral" />
      <bindingRedirect oldVersion="1.0.0.0"
                        newVersion="101.0.0.0"/>
    </dependentAssembly>
  </assemblyBinding>
</runtime>

Configuration

The following table shows the web.config configuration file of the PubsWebRole project.

 1: <?xml version="1.0"?>
 2: <configuration>
 3:   <configSections>
 4:     <!-- Append below entry to configSections. Do not overwrite the full section. -->
 5:     <section name="dataCacheClients"
 6:              type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, 
 7:  Microsoft.ApplicationServer.Caching.Core"
 8:              allowLocation="true"
 9:              allowDefinition="Everywhere"/>
 10:   </configSections>
 11: 
 12:   <!-- Diagnostics -->
 13:   <system.diagnostics>
 14:     <trace autoflush="true">
 15:       <listeners>
 16:         <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, 
 17:  Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 18:           name="AzureDiagnostics">
 19:           <filter type="" />
 20:         </add>
 21:       </listeners>
 22:     </trace>
 23:   </system.diagnostics>
 24: 
 25:   <!-- Caching -->
 26:   <!-- Cache exposes two endpoints: one simple and other SSL endpoint. 
 27:  Choose the appropriate endpoint depending on your security needs. -->
 28:   <dataCacheClients>
 29: 
 30:     <dataCacheClient name="default">
 31:       <localCache isEnabled="true" sync="TimeoutBased" ttlValue="300" />
 32:       <hosts>
 33:         <host name="babocache.cache.appfabriclabs.com" cachePort="22233" />
 34:       </hosts>
 35: 
 36:       <securityProperties mode="Message">
 37:         <messageSecurity
 38:           authorizationInfo="AUTHENTICATION TOKEN">
 39:         </messageSecurity>
 40:       </securityProperties>
 41:     </dataCacheClient>
 42: 
 43:     <dataCacheClient name="SslEndpoint">
 44:       <localCache isEnabled="true" sync="TimeoutBased" ttlValue="300" />
 45:       <hosts>
 46:         <host name="babocache.cache.appfabriclabs.com" cachePort="22243" />
 47:       </hosts>
 48: 
 49:       <securityProperties mode="Message" sslEnabled="true">
 50:         <messageSecurity
 51:           authorizationInfo="AUTHENTICATION TOKEN">
 52:         </messageSecurity>
 53:       </securityProperties>
 54:     </dataCacheClient>
 55: 
 56:   </dataCacheClients>
 57: 
 58:   <!-- Connection Strings -->
 59:   <connectionStrings>
 60:     <add name="PubsDatabase"
 61:          connectionString="Data Source=UPPY,1433;Initial Catalog=Pubs;Persist Security Info=True;…"
 62:          providerName="System.Data.SqlClient" />
 63:   </connectionStrings>
 64: 
 65:   <!-- Web -->
 66:   <system.web>
 67: 
 68:     <customErrors mode="Off"/>
 69:     <compilation debug="true" targetFramework="4.0" />
 70: 
 71:     <authentication mode="Forms">
 72:       <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
 73:     </authentication>
 74: 
 75:     <membership>
 76:       <providers>
 77:         <clear/>
 78:         <add name="AspNetSqlMembershipProvider"
 79:              type="System.Web.Security.SqlMembershipProvider"
 80:              connectionStringName="ApplicationServices"
 81:              enablePasswordRetrieval="false"
 82:              enablePasswordReset="true"
 83:              requiresQuestionAndAnswer="false"
 84:              requiresUniqueEmail="false"
 85:              maxInvalidPasswordAttempts="5"
 86:              minRequiredPasswordLength="6"
 87:              minRequiredNonalphanumericCharacters="0"
 88:              passwordAttemptWindow="10"
 89:              applicationName="/" />
 90:       </providers>
 91:     </membership>
 92: 
 93:     <profile>
 94:       <providers>
 95:         <clear/>
 96:         <add name="AspNetSqlProfileProvider"
 97:              type="System.Web.Profile.SqlProfileProvider"
 98:              connectionStringName="ApplicationServices"
 99:              applicationName="/"/>
 100:       </providers>
 101:     </profile>
 102: 
 103:     <roleManager enabled="false">
 104:       <providers>
 105:         <clear/>
 106:         <add name="AspNetSqlRoleProvider"
 107:              type="System.Web.Security.SqlRoleProvider"
 108:              connectionStringName="ApplicationServices"
 109:              applicationName="/" />
 110:         <add name="AspNetWindowsTokenRoleProvider"
 111:              type="System.Web.Security.WindowsTokenRoleProvider" a
 112:              pplicationName="/" />
 113:       </providers>
 114:     </roleManager>
 115: 
 116:   </system.web>
 117: 
 118:   <system.webServer>
 119:     <modules runAllManagedModulesForAllRequests="true">
 120:       <remove name="RewriteModule" />
 121:     </modules>
 122: 
 123:     <!-- enabling failed request tracing to illustrate diagnostics in Windows Azure
 124: 
 125:  For more information on failed request logging configuration, see:
 126:  http://www.iis.net/ConfigReference/system.webServer/tracing/traceFailedRequests/add -->
 127:     <tracing>
 128:       <traceFailedRequests>
 129:         <add path="Default.aspx">
 130:           <traceAreas>
 131:             <add provider="ASP" verbosity="Verbose" />
 132:             <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
 133:             <add provider="ISAPI Extension" verbosity="Verbose" />
 134:             <add provider="WWW Server"
 135:            areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module"
 136:                  verbosity="Verbose" />
 137:           </traceAreas>
 138:           <failureDefinitions statusCodes="400-599" />
 139:         </add>
 140:       </traceFailedRequests>
 141:     </tracing>
 142:   </system.webServer>
 143: 
 144:   <!-- WCF -->
 145:   <system.serviceModel>
 146: 
 147:     <!-- WCF Client Endpoints -->
 148:     <client>
 149:       <!-- Always invoke Pubs WCF service directly via Windows Azure Connect -->
 150:       <!-- NOTE: uppy is my machine name! -->
 151:       <endpoint name="DirectCallNoCache"
 152:                 address="http://uppy/Pubs/Service.svc"
 153:                 binding="basicHttpBinding"
 154:                 bindingConfiguration="basicHttpBinding"
 155:                 contract="PubsLocalService.IAuthors" />
 156:       <!-- Uses the caching channel to transparently retrieve data from Windows Azure AppFabric Caching -->
 157:       <endpoint name="UseWCFCachingChannel"
 158:                 address="http://uppy/Pubs/Service.svc"
 159:                 binding="customBinding"
 160:                 bindingConfiguration="customBinding"
 161:                 contract="PubsLocalService.IAuthors" />
 162:       <!-- Invoke Pubs WCF service using a basicHttpRelayBinding and no caching -->
 163:       <endpoint name="UseSBWithoutCaching"
 164:                 address="http://baboland.servicebus.appfabriclabs.com/pubs/basichttp"
 165:                 binding="basicHttpRelayBinding"
 166:                 bindingConfiguration="basicHttpRelayBinding"
 167:                 contract="PubsLocalService.IAuthors"/>
 168:       <!-- Invoke Pubs WCF service using a basicHttpRelayBinding with caching enabled -->
 169:       <endpoint name="UseSBWithCaching"
 170:                 address="http://baboland.servicebus.appfabriclabs.com/pubs/basichttp"
 171:                 binding="basicHttpRelayBinding"
 172:                 bindingConfiguration="basicHttpRelayBinding"
 173:                 behaviorConfiguration="cachingEndpointBehavior"
 174:                 contract="PubsLocalService.IAuthors"/>
 175:     </client>
 176: 
 177:     <!-- WCF Bindings -->
 178:     <bindings>
 179: 
 180:       <!-- customBinding -->
 181:       <customBinding>
 182:         <binding name="customBinding"
 183:                  closeTimeout="00:10:00"
 184:                  openTimeout="00:10:00"
 185:                  receiveTimeout="00:10:00"
 186:                  sendTimeout="00:10:00">
 187:           <clientCaching enabled="true"
 188:                          header="true"
 189:                          timeout="01:00:00"
 190:                          cacheType="AppFabricCache"
 191:                          maxBufferSize="65536"
 192:                          keyCreationMethod="MessageBody">
 193:             <operations>
 194:               <operation action="GetTitles"
 195:                          keyCreationMethod="MessageBody"
 196:                          cacheType="AppFabricCache"
 197:                          timeout="08:00:00" />
 198:             </operations>
 199:           </clientCaching>
 200:           <textMessageEncoding messageVersion="Soap11" />
 201:           <httpTransport />
 202:         </binding>
 203:       </customBinding>
 204: 
 205:       <!-- basicHttpBinding -->
 206:       <basicHttpBinding>
 207:         <binding name="basicHttpBinding"
 208:                  closeTimeout="00:10:00"
 209:                  openTimeout="00:10:00"
 210:                  receiveTimeout="00:10:00"
 211:                  sendTimeout="00:10:00"
 212:                  allowCookies="false"
 213:                  bypassProxyOnLocal="false"
 214:                  hostNameComparisonMode="StrongWildcard"
 215:                  maxBufferSize="10485760"
 216:                  maxBufferPoolSize="1048576"
 217:                  maxReceivedMessageSize="10485760"
 218:                  messageEncoding="Text"
 219:                  textEncoding="utf-8"
 220:                  transferMode="Buffered"
 221:                  useDefaultWebProxy="true">
 222:           <security mode="None">
 223:             <transport clientCredentialType="None" proxyCredentialType="None"
 224:               realm="" />
 225:             <message clientCredentialType="UserName" algorithmSuite="Default" />
 226:           </security>
 227:         </binding>
 228:       </basicHttpBinding>
 229: 
 230:       <!-- basicHttpRelayBinding -->
 231:       <basicHttpRelayBinding>
 232:         <binding name="basicHttpRelayBinding">
 233:           <security mode="None" relayClientAuthenticationType="None"/>
 234:         </binding>
 235:       </basicHttpRelayBinding>
 236:     </bindings>
 237: 
 238:     <!-- WCF Behaviors -->
 239:     <behaviors>
 240:       <!-- WCF Endpoint Behaviors -->
 241:       <endpointBehaviors>
 242:         <!-- basicHttpRelayBinding -->
 243:         <behavior name="cachingEndpointBehavior">
 244:           <!-- The attributes of the cachingBehavior element define default values for the caching channel -->
 245:           <cachingBehavior enabled="true"
 246:                            header="true"
 247:                            timeout="08:00:00"
 248:                            cacheType="AppFabricCache"
 249:                            maxBufferSize="65536"
 250:                            keyCreationMethod="Simple">
 251:             <operations>
 252:               <!-- The attributes of an operation element define the 
 253:  caching channel behavior for a given service action -->
 254:               <operation action="GetTitles"
 255:                          keyCreationMethod="Simple"
 256:                          cacheType="AppFabricCache"
 257:                          timeout="00:05:00" />
 258:             </operations>
 259:           </cachingBehavior>
 260:         </behavior>
 261:       </endpointBehaviors>
 262:     </behaviors>
 263: 
 264:     <!-- Register WCF Extensions -->
 265:     <extensions>
 266:       <behaviorExtensions>
 267:         <!-- This item is required to register the caching endpoint behavior -->
 268:         <add name="cachingBehavior"
 269:         type="Microsoft.AppFabric.CAT.Samples.WCF.ClientCache.ExtensionLibrary.ClientCacheBehaviorExtensionElement, 
 270:               Microsoft.AppFabric.CAT.Samples.WCF.ClientCache.ExtensionLibrary, 
 271:               Version=1.0.0.0, Culture=neutral, PublicKeyToken=8f6257ebc688af7c"/>
 272:         <!-- Thes following items are required to register the Service Bus behaviors -->
 273:         <add name="connectionStatusBehavior"
 274:              type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, 
 275:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 276:         <add name="transportClientEndpointBehavior"
 277:              type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, 
 278:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 279:         <add name="serviceRegistrySettings"
 280:              type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, 
 281:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 282:       </behaviorExtensions>
 283:       <bindingElementExtensions>
 284:         <!-- This item is required to register the caching binding element -->
 285:         <add name="clientCaching"
 286:         type="Microsoft.AppFabric.CAT.Samples.WCF.ClientCache.ExtensionLibrary.ClientCacheBindingExtensionElement, 
 287:               Microsoft.AppFabric.CAT.Samples.WCF.ClientCache.ExtensionLibrary, 
 288:               Version=1.0.0.0, Culture=neutral, PublicKeyToken=8f6257ebc688af7c"/>
 289:         <!-- Thes following items are required to register the Service Bus binding elements -->
 290:         <add name="tcpRelayTransport"
 291:              type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, 
 292:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 293:         <add name="httpRelayTransport"
 294:              type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, 
 295:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 296:         <add name="httpsRelayTransport"
 297:              type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, 
 298:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 299:         <add name="onewayRelayTransport"
 300:              type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, 
 301:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 302:       </bindingElementExtensions>
 303:       <!-- The following items are required to register the Service Bus bindings -->
 304:       <bindingExtensions>
 305:         <add name="basicHttpRelayBinding"
 306:              type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, 
 307:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 308:         <add name="webHttpRelayBinding"
 309:              type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, 
 310:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 311:         <add name="ws2007HttpRelayBinding"
 312:              type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, 
 313:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 314:         <add name="netTcpRelayBinding"
 315:              type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, 
 316:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 317:         <add name="netOnewayRelayBinding"
 318:              type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, 
 319:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 320:         <add name="netEventRelayBinding"
 321:              type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, 
 322:                    Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
 323:       </bindingExtensions>
 324:     </extensions>
 325: 
 326:   </system.serviceModel>
 327: 
 328:   <!-- Assembly Redirection -->
 329:   <runtime>
 330:     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
 331:       <dependentAssembly>
 332:         <assemblyIdentity name="Microsoft.ApplicationServer.Caching.Client"
 333:                           publicKeyToken="31bf3856ad364e35"
 334:                           culture="Neutral" />
 335:         <bindingRedirect oldVersion="1.0.0.0"
 336:                          newVersion="101.0.0.0"/>
 337:       </dependentAssembly>
 338:       <dependentAssembly>
 339:         <assemblyIdentity name="Microsoft.ApplicationServer.Caching.Core"
 340:                           publicKeyToken="31bf3856ad364e35"
 341:                           culture="Neutral" />
 342:         <bindingRedirect oldVersion="1.0.0.0"
 343:                          newVersion="101.0.0.0"/>
 344:       </dependentAssembly>
 345:     </assemblyBinding>
 346:   </runtime>
 347: </configuration>

Please find below a brief description of the main elements and sections of the configuration file:

  • Lines [4-10] define the config sections. For Windows Azure AppFabric Caching features to work, the configSections element must be the first element in the application configuration file. It must contain child elements that tell the runtime how to use the dataCacheClients element.
  • Lines [28-56] contain the dataCacheClients element that is used to configure the cache client. Child elements dataCacheClient define cache client configuration; in particular, the localCache element specifies the local cache settings.
  • Lines [148-175] contain the client section that defines a list of endpoints the test project uses to connect to the test service. In particular, I created 4 different endpoints to demonstrate how to configure the caching channel:
    • The first endpoint called DirectCallNoCache does not use the caching channel and always invokes the the underlying service directly using the Windows Azure Connect and the BasicHttpBinding.
    • The second endpoint called UseWCFCachingChannel uses the CustomBinding as a recipe to create the channel stack at runtime. The custom binding is composed of 3 binding elements: the clientCaching, textMessageEncoding and httpTransport. As you can see at lines [181-203], the clientCaching binding element allows to accurately configure the runtime behavior of the caching channel at a general level and on a per operation basis. Below I will explain in detail how to configure the clientCaching binding element.
    • The first endpoint called UseSBWithoutCaching does not use the caching channel and always invokes the the underlying service directly using the Service Bus and the BasicHttpRelayBinding.
    • The fourth endpoint called UseSBWithCaching adopts the Service Bus and the BasicHttpRelayBinding to communicate with the underlying service. However, the endpoint is configured to use the cachingBehavior that at runtime replaces the original binding with a CustomBinding made up of the same binding elements and adds the clientCaching binding element as the first element to the binding element collection. This technique is an alternative way to use and configure the caching channel.
  • Lines [265-324] contain the extensions element which defines the cachingBehavior extension element and the clientCaching binding element extension element. Besides, this section configures the WCF extensions introduced and required by the Service Bus (see the NOTE box below for more information on this).

As you can easily notice, both the cachingBehavior and clientCaching components share the same configuration that is defined as follows:

cachingBehavior and clientCaching elements:

  • enabled property: gets or sets a value indicating whether the WCF caching channel is enabled. When the value is false, the caching channel always invokes the target service. This property can be overridden at the operation level. This allows to enable or disable caching on a per operation basis.
  • headerproperty: gets or sets a value indicating whether a custom header is added to the response to indicate the source of the WCF message (cache or service). This property can be overridden at the operation level.
  • timeoutproperty: gets or sets the default amount of time the object should reside in the cache before expiration. This property can be overridden at the operation level.
  • cacheType property: gets or sets the cache type used to store items. The component actually supports two caching providers: AppFabricCache and WebCache. This property can be overridden at the operation level.
  • maxBufferSizeproperty: gets or sets the maximum size in bytes for the buffers used by the caching channel. This property can be overridden at the operation level.
  • indexes property: gets or sets a string containing a comma-separated list of indexes of parameters to be used to compute the cache key. This property is used only when the keyCreationMethod= Indexed.
  • keyCreationMethodproperty: gets or sets the method used to calculate the key for cache items. The component provides 5 key creation methods:
    • Action: this method uses the value of the Actionheader of the request as key for the response. For obvious reasons, this method can be used only for operations without input parameters.
    • MessageBody: this method uses the body of the request as key for the response. This method doesn’t work when the request message contains contains DateTimeelements that could vary from call to call.
    • Simple: this method creates the string [A](P1)(P2)…(Pn)for an operation with n parameters P1-Pn and Action = A.
    • Indexed: this method works as the Simple method, but it allows to specify which parameters to use when creating the key. For example, the Indexed method creates the string [A](P1)(P3)(5) for an operation with n parameters P1-Pn (n >= 5) and Action = A and when the value of the Indexesproperty is equal to “1, 3, 5”. This method can be used to skip DateTime parameters from the compute of the key.
    • MD5: this method uses the MD5 algorithm to compute a hash from the body of the request message.

operation element:

  • action property: gets or sets the WS-Addressing action of the request message.
  • enabled property: gets or sets a value indicating whether the WCF caching channel is enabled for the current operation identified by the Actionproperty.
  • headerproperty: gets or sets a value indicating whether a custom header is added to the response to indicate the source of the WCF message (cache or service) at the operation level.
  • timeoutproperty: gets or sets the default amount of time the object should reside in the cache before expiration at the operation level.
  • cacheType property: gets or sets the cache type used to store responses for the current operation. The component actually supports two caching providers: AppFabricCache and WebCache. This property can be overridden at the operation level.
  • maxBufferSizeproperty: gets or sets the maximum size in bytes for the buffers used by the caching channel for the current operation.
  • indexes property: gets or sets a string containing a comma-separated list of indexes of parameters to be used to compute the cache key for the current operation. This property is used only when the keyCreationMethod= Indexed.
  • keyCreationMethod property: gets or sets the method used to calculate the key for cache items.

 

Quotes_Icon NOTE
When you install the Windows Azure AppFabric SDK on your development machine, the setup process registers the Service Bus relay bindings, binding elements and behaviors as WCF extensions in the machine.config. However, these extensions are not installed by default in a Windows Azure VM when you deploy an Windows Azure application to the cloud. The documentation on MSDN suggests to perform the following steps when you develop a Windows Azure-Hosted application that uses the Service Bus to invoke a remote service:

  1. In Solution Explorer, under the WorkerRole or WebRole node (depending on where you have your code), add the Microsoft.ServiceBus assembly to your Windows Azure project as a reference.
    This step is the standard process for adding a reference to an assembly.

  2. In the Reference folder, right-click Microsoft.ServiceBus. Then click Properties.

  3. In the Properties dialog, set Copy Local to True. Doing so makes sure that the Microsoft.ServiceBus assembly will be available to your application when it runs on Windows Azure.

  4. In your ServiceDefinition.csdef file, set the enableNativeCodeExecution field to true.

However, the above steps are not sufficient. In order to use the relay bindings in a Web Role or a Worker Role running in the cloud, you need to make sure to properly register them as WCF extensions in the application configuration file or use a Startup Task to register them in the machine.config. In this case, make sure to add the following XML snippet to the configuration/system.serviceModel section of the configuration file.

<!-- Register WCF Extensions -->
    <extensions>
      <behaviorExtensions>
        <!-- Thes following items are required to register the Service Bus behaviors -->
        <add name="connectionStatusBehavior"
             type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement,
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral,  
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="transportClientEndpointBehavior"
             type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="serviceRegistrySettings"
             type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
      </behaviorExtensions>
      <bindingElementExtensions>
        <!-- Thes following items are required to register the Service Bus binding elements -->
        <add name="tcpRelayTransport"
             type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpRelayTransport"
             type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpsRelayTransport"
             type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="onewayRelayTransport"
             type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
      </bindingElementExtensions>
      <!-- The following items are required to register the Service Bus bindings -->
      <bindingExtensions>
        <add name="basicHttpRelayBinding"
             type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="webHttpRelayBinding"
             type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="ws2007HttpRelayBinding"
             type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="netTcpRelayBinding"
             type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="netOnewayRelayBinding"
             type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
        <add name="netEventRelayBinding"
             type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, 
                   Microsoft.ServiceBus, Version=2.0.0.0, Culture=neutral, 
                   PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
    </extensions>

 

Source Code

This section contains the code of the main classes of the solution. You can find the rest of the components in the source code that accompanies this article. The following table contains the source code for the Service.aspx page running in the web role.

namespace PubsWebRole
{
    public enum CallMode
    {
        DirectCall,
        UseCachingAPIExplicitly,
        UseCachingAPIViaWCFChannel,
        UseSBWithoutCaching,
        UseSBWithCaching
    }

    public partial class Service : System.Web.UI.Page
    {
        #region Static Fields
        private static DataCacheFactory dataCacheFactory = new DataCacheFactory();
        #endregion

        #region Protected Methods
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                TitlesGridView.DataSource = new List<Book>();
                TitlesGridView.DataBind();
            }
        }

        protected void AuthorsDataSource_Selecting(object sender,
                                                   SqlDataSourceSelectingEventArgs e)
        {
            e.Command.CommandTimeout = 300;
        }

        protected void GetTitles_Click(object sender, EventArgs e)
        {
            CallMode callMode;
            if (DirectCall.Checked)
            {
                callMode = CallMode.DirectCall;
            }
            else if (UseCachingAPIExplicitly.Checked)
            {
                callMode = CallMode.UseCachingAPIExplicitly;
            }
            else if (UseCachingAPIViaWCFChannel.Checked)
            {
                callMode = CallMode.UseCachingAPIViaWCFChannel;
            }
            else if (UseSBWithoutCaching.Checked)
            {
                callMode = CallMode.UseSBWithoutCaching;
            }
            else
            {
                callMode = CallMode.UseSBWithCaching;
            }
            TitlesGridView.DataSource = GetTitles(Authors.SelectedValue,
                                                  callMode);
            TitlesGridView.DataBind();
        }
        #endregion

        #region Private Methods
        private List<Book> GetTitles(string authorId, CallMode callMode)
        {
            if (string.IsNullOrEmpty(authorId))
            {
                return new List<Book>();
            }

            bool faulted = false;
            AuthorsClient proxy = null;
            List<Book> bookList = null;
            Book[] books = null;

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                switch (callMode)
                {
                    case CallMode.UseCachingAPIExplicitly:
                        // Use Caching API Explicitly and via Windows Azure Connect.
                        // CACHE ASIDE PATTERN: the application will first 
                        // check to see if the data is in the cache. 
                        // If not, it will invoke the underlying WCf service, 
                        // write the data in the cache, then return the value. 
                        DataCache dataCache = null;
                        string key = string.Format("GetTitles_{0}", authorId);
                        // The dataCacheFactory is created once in the static 
                        // constructor for performance reasons
                        if (dataCacheFactory != null)
                        {
                            dataCache = dataCacheFactory.GetDefaultCache();
                            if (dataCache != null)
                            {
                                bookList = dataCache.Get(key) as List<Book>;
                            }
                        }
                        if (bookList != null)
                        {
                            return bookList;
                        }
                        else
                        {
                            proxy = new AuthorsClient("DirectCallNoCache");
                            books = proxy.GetTitles(authorId);
                            if (books != null)
                            {
                                bookList = new List<Book>(books);
                                if (dataCache != null)
                                {
                                    dataCache.Put(key, bookList);
                                }
                                return bookList;
                            }
                        }
                        break;
                    case CallMode.UseCachingAPIViaWCFChannel:
                        // UUse Caching API via WCF channel and Windows Azure Connect.
                        proxy = new AuthorsClient("UseWCFCachingChannel");
                        books = proxy.GetTitles(authorId);
                        if (books != null)
                        {
                            return new List<Book>(books);
                        }
                        break;
                    case CallMode.DirectCall:
                        // Directly invoke the WCF service via Windows Azure Connect.
                        proxy = new AuthorsClient("DirectCallNoCache");
                        books = proxy.GetTitles(authorId);
                        if (books != null)
                        {
                            return new List<Book>(books);
                        }
                        break;
                    case CallMode.UseSBWithCaching:
                        // Use Caching API via WCF channel and Service Bus.
                        proxy = new AuthorsClient("UseSBWithCaching");
                        books = proxy.GetTitles(authorId);
                        if (books != null)
                        {
                            return new List<Book>(books);
                        }
                        break;
                    default:
                        // Directly invoke the WCF service via Service Bus.
                        proxy = new AuthorsClient("UseSBWithoutCaching");
                        books = proxy.GetTitles(authorId);
                        if (books != null)
                        {
                            return new List<Book>(books);
                        }
                        break;
                }
            }
            catch (FaultException ex)
            {
                faulted = true;
                MessageLiteral.Text = string.Format("<b>Exception</b>: {0}", ex.Message);
                if (proxy != null)
                {
                    proxy.Abort();
                }
            }
            catch (CommunicationException ex)
            {
                faulted = true;
                MessageLiteral.Text = string.Format("<b>Exception</b>: {0}", ex.Message);
                if (proxy != null)
                {
                    proxy.Abort();
                }
            }
            catch (TimeoutException ex)
            {
                faulted = true;
                MessageLiteral.Text = string.Format("<b>Exception</b>: {0}", ex.Message);
                if (proxy != null)
                {
                    proxy.Abort();
                }
            }
            catch (Exception ex)
            {
                faulted = true;
                MessageLiteral.Text = string.Format("<b>Exception</b>: {0}", ex.Message);
                if (proxy != null)
                {
                    proxy.Abort();
                }
            }
            finally
            {
                stopwatch.Stop();
                if (!faulted)
                {
                    MessageLiteral.Text = string.Format(

 

                                                      "<b>CallMode</b>: {0}</br><b>Elapsed Time (milliseconds)</b>: {1}",
                                                      callMode,
                                                      stopwatch.ElapsedMilliseconds);
                }
            }
            return new List<Book>();
        }
        #endregion
    }
}

The following table contains the source code for the Cache class in the AppFabricCache project. This class implements the Windows Azure AppFabric Caching caching provider used by the caching channel.

namespace Microsoft.AppFabric.CAT.Samples.WCF.ClientCache.AppFabricCache
{
    public static class Cache
    {
        #region Private Static Fields
        private static DataCache dataCache;
        #endregion

        #region Static Constructor
        static Cache()
        {
            try
            {
                DataCacheFactory dataCacheFactory = new DataCacheFactory();
                dataCache = dataCacheFactory.GetDefaultCache();
            }
            catch (Exception ex)
            {
                ExceptionHelper.HandleException(ex);
            }
        }
        #endregion

        #region Public Static Methods
        /// <summary>
        /// Adds an object to the cache.
        /// </summary>
        /// <typeparam name="T">Type of item being added.</typeparam>
        /// <param name="key">The unique value that is used to identify the object in the cache.</param>
        /// <param name="value">The object saved to the cache cluster.</param>
        /// <param name="timeout">The amount of time the object should reside in the cache before expiration.</param>
        public static void Add<T>(string key, T value, TimeSpan timeout)
        {
            if (value == null)
            {
                return;
            }

            dataCache.Put(key, value, timeout);
        }

        /// <summary>
        /// Used to retrieve an item from the cache.
        /// </summary>
        /// <typeparam name="T">Type of item being added.</typeparam>
        /// <param name="key">The unique value that is used to identify the object in the cache.</param>
        /// <returns>An item of type T if found in the cache using the key provided, null otherwise.</returns>
        public static T Get<T>(string key)
        {
            return (T)dataCache.Get(key);
        }
        #endregion
    }
}

 

Performance Results

The following performance results have been collected under the following condition:

  • The AuthorsWebService and the pubs database were running on my laptop in Milan, Italy.
  • The Web Role and Windows Azure AppFabric Caching were running in the South Central US Azure Data Center. I intentionally deployed the Web Role in US and not in Western Europe to emphasize the distance between the cloud application and the remote service.
  • The local cache was enabled in the cache client configuration.

 

Call Mode Average Call Time (milliseconds)
Directly invoke the WCF service via Windows Azure Connect 355
Use Caching API Explicitly and via Windows Azure Connect 2
Use Caching API via caching channel and Windows Azure Connect 5
Directly invoke the WCF service via Service Bus 415
Use Caching API via caching channel and Service Bus 5

 

Quotes_Icon NOTE
According to my tests, invoking a WCF service running on-premises in the organization's network from a Web Role using Windows Azure Connect and the BasicHttpBinding is slightly faster than invoking the same service via the Service Bus and the BasicHttpRelayBinding. Nevertheless, more accurate and exhaustive performance tests should be conducted to confirm this result.

 

Conclusions

The caching channel shown in this article can be used to transparently inject client-side caching capabilities into an existing Windows Azure application that uses WCF to invoke one or multiple back-end services. The use of client-side caching techniques allows to dramatically increase performance and reduce the costs due to the use of Windows Azure Connect and Service Bus. The source code that accompanies the article can be downloaded here. As always, any feedback is more than welcome!

Additional Resources/References

For more information on the topic discussed in this blog post, please refer to the following: