Il WCF REST Starter Kit rende disponibile tra le varie utility anche una semplice soluzione per abilitare il web caching per WCF sfruttando l'interazione con la proprietà HttpResponse.Cache di ASP.NET. La soluzione si basa sull'utilizzo di un attributo WebCache con cui decorare i nostri OperationContract che sono suscettibili a meccanismi di caching secondo una specifica profilatura. L'attributo è definito all'interno della library Microsoft.ServiceModel.Web.dll fornita con il Kit. Vediamo nel dettaglio i vari aspetti di utilizzo aiutandoci con un semplice esempio: la pubblicazione di un feed RSS 2.0 che gestisca un meccanismo di caching web in base al numero di item richiesti (es. 10, 20 etc.. ).
Supponiamo di avere un ServiceContract come il seguente:
[ServiceContract(SessionMode=SessionMode.NotAllowed, ProtectionLevel=ProtectionLevel.None)]
public interface IServiceRSS
{
[WebCache(CacheProfileName = "RssCacheDefault")]
[WebGet(UriTemplate = "?numItems={num}")]
[OperationContract]
Rss20FeedFormatter GetRss20(int num);
}
Notiamo subito la presenza dell'attributo WebCache la cui proprietà CacheProfileName serve per specificare il nome della nostra profilatura di caching (es. "RssCacheDefault"). Il comportamento di caching è configurabile sia valorizzando da codice le proprietà dell'attributo WebCache (vedi figura sotto) che dichiarativamente nella sezione outputCacheProfiles di ASP.NET.
Qua sotto viene riportato un esempio di configurazione per il nostro l'esempio.
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<clear/>
<add name="RssCacheDefault" duration="60" enabled="true" location="ServerAndClient" varyByParam="numItems"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
...
</system.web>
...
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="TestCachingApp.SampleServiceRSS" behaviorConfiguration="SampleServiceRSSBehavior" >
<endpoint address="" binding="webHttpBinding" contract="TestCachingApp.IServiceRSS"></endpoint>
...
</service>
</services>
...
</system.serviceModel>
E' importante notare come occorra abilitare la compatibilità dell'ambiente di hosting del servizio WCF con ASP.NET, impostando a "true" l'attributo aspNetCompatibilityEnabled del tag serviceHostingEnvironment.
A questo punto il gioco è fatto!
Nell'implementazione del nostro servizio dobbiamo porre attenzione ad impostare la compatibilità con ASP.NET mediante l'attributo AspNetCompatibilityRequirements :
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class SampleServiceRSS : IServiceRSS
{
public Rss20FeedFormatter GetRss20(int num)
{
SyndicationFeed feed = new SyndicationFeed("Title: ...", "Description: ...", new Uri("http://tempuri.org"));
...
// Initialize the feed with 'num' items
...
return new Rss20FeedFormatter(feed);
}
}
Poiché l'uso di un meccanismo di caching costituisce una prassi ormai ordinaria nei vari paradigmi di programmazione Web, questo tipo di soluzione che integra la Web Cache di ASP.NET all'interno di WCF costituisce sicuramente un vantaggio significativo soprattutto per motivi pratici. Sebbene esistano scenari che richiedono soluzioni di natura diversa (es. Enterprise Library Caching Block :D ), questo approccio può risolvere elegantemente i casi più comuni.
Behind the Scenes
Per chi fosse interessato ad approfondire l'argomento riporto alcune note secondo me interessanti.
L'attributo WebCache è implementato come un operation behavior di WCF ed implementa l'interfaccia IOperationBehavior. Il trucco si trova nell'implementazione del metodo ApplyDispatchBehavior di tale interfaccia: ogni volta che a run-time viene invocato un metodo decorato con l'attibuto WebCache, il nostro eventuale profilo di caching (ovvero un oggetto di tipo Systen.Web.Configuration.OutputCacheProfile) viene passato ad un' istanza di una classe utility del kit: CachingParameterInspector (che implementa IParameterInspector). Questa classe in pratica non fa altro che provvedere alla configurazione della proprietà HttpContext.Current.Response.Cache.
L'aspetto interessante si trova proprio a questo punto: la classe CachingParameterInspector creata viene aggiunta alla collection di ParameterIspector dell' oggetto DispatchOperation passato in input come argomento del metodo. In questo modo il runtime scatenerà l'esecuzione degli algoritmi di caching all'interno dell'infrastruttura di ASP.NET secondo la nostra eventuale profilatura.