in

DotNetSide

Dot Net South Italy Developers User Group

Articoli

Articoli pubblicati dagli iscritti a .netSide

Estendere Windows Communication Foundation: I Behaviors

Autore: Fabio Cozzolino

L’architettura di Windows Communication Foundation (WCF) è stata realizzata per consentire un elevato grado di estensibilità. In effetti ogni applicazione ha le sue esigenze e spesso la scelta dell’utilizzo di un framework piuttosto che di un altro si basa sulla valutazione di come esso può esserci d’aiuto nei differenti scenari. WCF consente di realizzare applicazioni utilizzando sia un set di configurazioni di base (o comunque guidate), sia concedendo la possibilità di personalizzare il comportamento del nostro servizio. Una di queste possibilità è fornita dai behaviors. In questo articolo vedremo come costruire un semplice Interceptor che si occupa di tracciare tutti quei messaggi che rispettano determinate caratteristiche.

Cosa sono i Behaviors

Nel primo screencast della serie su WCF (Address, Binding, Contract) abbiamo parlato dell’ABC di WCF. Questi tre elementi, che costituiscono l'endpoint del servizio, nel 99% dei casidevono essere noti al client per poter avviare una corretta comunicazione tra le parti. Oltre all'endpoint, i behaviors consentono di intervenire nel channel stack per modificare il comportamento del nostro servizio o, in alcuni casi, anche del proxy client, sempre se sviluppato con WCF. Possiamo, ad esempio, intercettare il messaggio e verificare se sono state fornite le credenziali richieste, oppure possiamo impostare il comportamento del DataContractSerializer.

WCF consente lo sviluppo di quattro tipologie di behaviors, ognuna con le proprie caratteristiche e realizzabile attraverso l’implementazione della rispettiva interfaccia:

  • IServiceBehavior: l’implementazione di questa interfaccia consente di intervenire sull’intero service runtime, modificandone il comportamento. Non è utilizzabile lato client;
  • IEndpointBehavior: agisce unicamente a livello di endpoint, personalizzandone il comportamento sia lato client che lato server;
  • IContractBehavior: consente la personalizzazione sia del ClientRuntime che del DispatchRuntime rispettivamente sulle applicazioni client e service applications;
  • IOperationBehavior: consente la personalizzazione sia del ClientOperation che del DispatchOperation rispettivamente sulle applicazioni client e service;

In particolare, in questo articolo, implementeremo un IServiceBehavior che si occuperà di tenere traccia dei messaggi recapitati al servizio inviati da uno specifico client.

Primo passo: sviluppiamo il nostro Inspector

Prima di realizzare il nostro custom behavior dobbiamo sviluppare il componente che si occuperà di ispezionare il messaggio in ingresso, di valutarne il contenuto ed infine prendere la decisione adeguata. Per questo scopo WCF introduce il concetto di MessageInspector. Implementando l’interfaccia IDispatchMessageInspector, siamo in grado di intervenire nel DispatchRuntime, e quindi eslusivamente nel contesto di esecuzione del servizio, iniettando del nostro codice. Nel nostro caso specifico realizziamo il LoggerMessageInspector come di seguito:

public class LoggerMessageInspector : IDispatchMessageInspector
{
public LoggerMessageInspector()
{
}

#region IDispatchMessageInspector Members

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message
request, System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader("Requester", "http://dotnetside.org");
if (headerIndex == -1)
return null;

string content = request.Headers.GetHeader<string>(headerIndex);

if (content == "fabio")
{
// Leggiamo il messaggio e ne inseriamo il contenuto
MessageBuffer messageBuffer = request.CreateBufferedCopy(int.MaxValue);

// copiamo il contenuto del messaggio in uno stream
MemoryStream ms = new MemoryStream();
messageBuffer.WriteMessage(ms);

// dallo stream ricaviamo un array di byte rappresentante il messaggio
byte[] rawData = new byte[ms.Length];
ms.Write(rawData,
0, rawData.Length);

// scriviamo il risultato nell'EventLog
EventLog.WriteEntry("MessageLog", "Nuovo messaggio da fabio.",
EventLogEntryType.Information,
60000, short.MinValue,
rawData);
}

return null;
}

public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply,
object correlationState)
{
// do nothing
}

#endregion
}

 

Come potete vedere dal codice, l’interfaccia IDispatchMessageInspector richiede l’implementazione di due metodi che intervengono in due momenti differenti:

· AfterReceiveRequest

· BeforeSendReply

Il primo, come è facile capire dal nome, viene eseguito al ricevimento della richiesta, mentre il secondo prima di inviare una risposta al client. Nel nostro esempio stiamo utilizzando solo il primo, ma avremmo potuto migliorare l’esempio, tracciando nell’EventLog anche l’invio di una risposta a seguito della richiesta pervenuta da quello specifico Uri.

Dopo aver creato il MessageInspector, passiamo alla realizzazione del behavior. Lo scopo del behavior è principalmente quello di “agganciare” determinati comportamenti al contesto di esecuzione, rappresentato dal ServiceHost in esecuzione e dal ServiceDescription associato a quello specifico servizio. L’implementazione dell’interfaccia IServiceBehavior, infatti, richiede la realizzazione di tre metodi:

· AddBindingParameters: consente di aggiungere uno o più parametri personalizzati e richiesti dall’implementazione del channel in uso;

· ApplyDispatchBehavior: è il metodo principale e consente di eseguire la maggior parte del lavoro del behavior. E’ possibile aggiungere qui i message interceptor, come nel nostro esempio, gestori di evento, estensioni per la gestione della sicurezza o qualsiasi altra implementazione personalizzata;

· Validate: è il primo metodo richiamato dal runtime e permette la validazione del contesto di esecuzione sulla base della valutazione delle proprie necessità;

Sulla base di quanto detto, proviamo ad implementare il nostro behavior. Dei metodi sopra elencati, implementeremo per il nostro scopo solo l’ApplyDispatchBehavior:

public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.MessageInspectors.Add(
new LoggerMessageInspector());
}
}
}

Il codice mostrato aggiunge un’istanza del LoggerMessageInspector ad ogni EndpointDispatcher presente in tutti i ChannelDispatcher in ascolto sul service host in uso.

Utilizziamo il LoggerBehavior

Il codice mostrato realizza il behavior che ci eravamo prefissati. Al momento, però, possiamo solo utilizzare la classe agganciandola al relativo service host. Per utilizzare il LoggerBehavior anche nel file di configurazione, possiamo estendere la classe base BehaviorExtensionElement ed implementare gli override richiesti:

public class LoggerBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(LoggerBehavior); }
}

protected override object CreateBehavior()
{
return new LoggerBehavior();
}
}

Infine utilizziamo il file .config per registrare il behavior sviluppato ed associarne l’uso allo specifico endpoint/servizio attraverso l’infrastruttura di WCF:

 

<system.serviceModel>
...

<behaviors>
<serviceBehaviors>
<behavior name="helloServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<dataContractSerializer
ignoreExtensionDataObject="true"/>
<loggerMessageInspector/>
</behavior>
</serviceBehaviors>
</behaviors>

<extensions>
<behaviorExtensions>
<add name="loggerMessageInspector"
type
="extensions.LoggerBehaviorElement, extensions, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null"
/>
</behaviorExtensions>
</extensions>

</system.serviceModel>

Conclusioni

In questo articolo abbiamo visto come l’architettura di WCF permette di inserire codice personalizzato direttamente nel channel stack utilizzando i Message Inspector. Il codice mostrato nell’articolo può essere senz’altro migliorato ed integrato con ulteriori aspetti, ma costituisce sicuramente una base su cui sviluppare le nostre estensioni. E’ molto importante, oltre all’esempio in sé, comprendere le caratteristiche di WCF e soprattutto i punti in cui intervenire per personalizzarne il comportamento. Potete ovviamente chiedere chiarimenti o dubbi lasciando un commento in fondo a questa pagina, oppure utilizzando il form contact del mio blog.

Only published comments... Apr 09 2007, 04:44 PM by DotNetSide Staff
Filed under: ,

Comments

 

Weblog di Fabio Cozzolino said:

Da qualche ora &#232; disponibile onlineil mio articolodedicato ai behaviors. Si tratta di una semplice overview

April 10, 2007 8:02 PM
Powered by Community Server (Commercial Edition), by Telligent Systems