Sei sulla pagina 1di 10

SERVIZI WCF: CREAZIONE, HOSTING E CONSUMO

Windows Communication Foundation (WCF), conosciuto in fase di sviluppo con il nome in codice Indigo, un "sottosistema applicativo" proprietario della Microsoft, che offre la struttura API per la creazione di applicazioni distribuite in ambienti Windows. Un servizio WCF si basa sugli '"EndPoint", che sono le porte attraverso le quali le applicazioni comunicano con il mondo esterno; si pu quindi affermare che un servizio WCF sia una collezione di EndPoint. A sua volta, un Endpoint costituito da quelli che sono i pilastri di WCF: "Address", "Binding", "Contract". In questa guida sar illustrato come creare un servizio WCF pronto per essere consumato da unapplicazione client. LIDE utilizzato Microsoft Visual Studio 2010. Descrizione del servizio

Il semplice servizio ha il compito di effettuare loperazione di divisione tra due numeri, numeratore e denominatore, passati come input. Ovviamente, se il denominatore risulta essere uguale a zero, dovr essere generata uneccezione da restituire al client. Per rendere lesercizio il pi completo possibile, nellimplementazione saranno utilizzate tutte le diverse tipologie di contatti previste da WCF: ServiceContract, OperationContract, MessageContract, FaultContract. Creazione della Soluzione

Dalla schermata principale di Visual Studio, File > New > Project. Scegliere il template Blank Solution, localizzato sotto la voce Other Project Types > Visual Studio Solution e nominarlo WcfServiceCalculator.

Sulla soluzione appena creata, Right-click e Add > New Project. Dalla schermata dei template proposti, scegliere WCF Service Library, localizzato sotto Visual C# > WCF. Nominare il servizio ElaborareDivisione.

In Solution Explorer sar visibile il Progetto appena creato con unalberatura di file auto-generati dal tool:

App.config, il file di configurazione del servizio, dove sono configurati gli endpoint; IService.cs, il file che descrive linterfaccia del servizio; Service.cs, il file che contiene la logica applicativa del servizio. Rinominare gli ultimi due file con IElaborareDivisione.cs e ElaborareDivisione.cs, rispettivamente. Definire linterfaccia del servizio

Definire linterfaccia di un servizio significa specificare il Contract, cio il cosa fa il servizio. In pratica la rappresentazione dei servizi e delle operazioni richiamabili ed utilizzabili da un fruitore. Come detto, il servizio ha unoperazione che effettua la divisione tra due numeri e restituisce il risultato; per questo semplice servizio linterfaccia dovrebbe essere la seguente:

[ServiceContract(Name="ElaborareDivisioneService")] public interface IElaborareDivisione { [OperationContract] [FaultContract(typeof(string))] double elaboraDivisione(double num, double den); }

Per, per rendere lesempio il pi completo possibile, complicheremo la definizione dellinterfaccia introducendo gli altri tipi di contratti previsti in WCF. Quindi andremo a scrivere il codice nel file IElaborareDivisione.cs:
[ServiceContract(Name="ElaborareDivisioneService")] public interface IElaborareDivisione { [OperationContract] [FaultContract(typeof(string))] RichiestaElaborazioneDivisioneResponse elaboraDivisione(RichiestaElaborazioneDivisione input); }

Il nome specificato nellattributo ServiceContract quello che apparir nellelemento portType del WSDL. Per loperazione stato specificato un fault, formattato come una stringa e per definire i parametri di input e output sono stati utilizzati i MessageContract, come mostrato di seguito:
[MessageContract] public class RichiestaElaborazioneDivisioneRequest { [MessageBodyMember] public Divisione fattori; } [MessageContract] public class RichiestaElaborazioneDivisioneResponse { [MessageBodyMember] public double risultato; }

Il messaggio del parametro di output RichiestaElaborazioneDivisioneResponse contiene nel body solo il risultato della divisione, un double; mentre il messaggio del parametro di input RichiestaElaborazioneDivisioneRequest contiene nel body un tipo di dato complesso, Divisione, che specifica i due fattori numeratore e denominatore; anche qui la complicazione giustificata dallutilizzo dei DataContract:
[DataContract] public class Divisione { [DataMember] public double numeratore; [DataMember] public double denominatore; }

Di seguito il codice completo del file IElaborareDivisione.cs:


using using using using using using System; System.Collections.Generic; System.Linq; System.Runtime.Serialization; System.ServiceModel; System.Text;

namespace ElaborareDivisione { [ServiceContract(Name = "ElaborareDivisioneService")]

public interface IElaborareDivisione { [OperationContract] [FaultContract(typeof(string))] RichiestaElaborazioneDivisioneResponse elaboraDivisione(RichiestaElaborazioneDivisioneRequest input); } [MessageContract] public class RichiestaElaborazioneDivisioneRequest { [MessageBodyMember] public Divisione fattori; } [MessageContract] public class RichiestaElaborazioneDivisioneResponse { [MessageBodyMember] public double risultato; } [DataContract] public class Divisione { [DataMember] public double numeratore; [DataMember] public double denominatore; } }

Implementazione del servizio

Definita linterfaccia del servizio, manca da specificare il suo comportamento, cio cosa accade internamente lato server quando un client esterno invoca il servizio. Programmaticamente non significa altro che implementare il metodo dellinterfaccia appena definita, andando a scrivere nel file ElaborareDivisione.cs:

public class ElaborareDivisione : IElaborareDivisione { public RichiestaElaborazioneDivisioneResponse elaboraDivisione(RichiestaElaborazioneDivisioneRequest input) { if (input.fattori.denominatore == 0) { string msg = "Divisione per 0 non consentita!"; throw new FaultException<string>(msg); } RichiestaElaborazioneDivisioneResponse output = new RichiestaElaborazioneDivisioneResponse(); output.risultato = (input.fattori.numeratore) / (input.fattori.denominatore); return output; } }

Hosting del servizio

Dopo aver sviluppato il nostro servizio, occorre trovare il modo di renderlo disponibile al mondo esterno; per questo abbiamo bisogno che il servizio sia ospitato allinterno di un processo windows chiamato processo host. In ogni processo, come previsto dal .NET Framework, possono esistere pi di un Application Domain, e allinterno di ognuno di essi possono essere ospitati anche pi di un servizio WCF.

Ci sono due principali opzioni per hostare un servizio: Self-hosting o Console application o Windows application o Windows services IIS o IIS 6 solo per http protocol o Windows Activation Service (WAS) o Windows Server AppFabric

In questarticolo useremo una Console application dove hostare il nostro servizio. Creiamo, quindi, un nuovo progetto di tipo Console application da aggiungere alla soluzione corrente e nominiamolo MyHost.

In Solution Explore sar visibile il progetto appena creato:

Prima di procedere a definire lhost, occorre aggiungere le referenze a System.ServiceModel e al servizio ElaboeareDivisione appena creato, cliccando con il tasto destro su References e scegliendo add Reference. Procediamo a modificare il main della classe Program:
static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(ElaborareDivisione.ElaborareDivisione))) { host.Open(); Console.WriteLine("Host aperto..."); Console.WriteLine("Premi un tasto per terminare"); Console.ReadKey(true); host.Close(); } }

Questo frammento di codice pu aprire unistanza del servizio ElaborareDivisione e la lascia aperta fino a che non viene premuto il tasto invio. Non resta altro che specificare un endpoint per il service host. Questo pu essere fatto programmaticamente con il codice o dichiarativamente attraverso un file di configurazione. Aggiungiamo un file chiamato App.config al progetto MyHost e ricopiamo al suo interno il contenuto del file chiamato con lo stesso nome presente nel progetto ElaborareDivisione, creato automaticamente quando stato compilato il servizio. Modifichiamo lindirizzo dove sar esposto il servizio, sostituendo la stringa nellelemento baseAddress del file App.config:
<add baseAddress="http://localhost:8020/ElaborareDivisioneService" />

Possiamo ora verificare il funzionamento dellhost appena creato, compilando la soluzione e cliccando sul file MyHost.exe, presente vostro_Path_Progetti_VisualStudio\WcfServiceCalculator\MyHost\bin\Debug; il risultato deve essere il seguente:

Consumo del servizio

Definito lendpoint del servizio e lhost che lo ospita, iniziamo ad interagire con esso. Procederemo creando un client che richiama il servizio e manipola il risultato ricevuto. In questo scenario necessario una classe proxy che ul client si occupa di inoltrare le richieste e formattare i messaggi. Per generare i proxy client abbiamo due possibilit: direttamente con le funzionalit offerte da VS 2010 o tramite il tool da linea di comando SvcUtil.exe. Iniziamo col creare il client, aggiungendo un nuovo progetto Console Application alla Soluzione, e chiamiamolo MyClient:

Generiamo ora i proxy client seguendo il primo metodo descritto, utilizziamo cio linterfaccia fornita da VS 2010 cliccando con il destro sul progetto e selezionando Add Service Reference. Nella finestra che apparir dobbiamo indicare lindirizzo al quale si trova esposto il servizio (quello inserito nel file App.config dellhost) e non dobbiamo dimenticare di tirar su lhost che ospita il servizio, altrimenti verr generato un errore (eseguire il file MyHost.exe):

Confermando sar creato il riferimento al servizio, con la generazione automatica del proxy e dei file XSD e WSDL che descrivono i metadati del servizio:

Creato il proxy, non resta che implementare le attivit svolte dal client per richiamare il servizio e ottenere il risultato. Scriviamo il codice nel file Program.cs presente in MyClient, specificando in input numeratore e denominatore e stampando il risultato della divisione ottenuto dalla chiamata al servizio; in caso di denominatore uguale a zero sar restituita leccezione definita nellinterfaccia del servizio:
class Program { static void Main(string[] args) { ElaboraDivisioneServiceReference.ElaborareDivisioneServiceClient client = new ElaboraDivisioneServiceReference.ElaborareDivisioneServiceClient(); Console.WriteLine("Inserisci numeratore"); double num = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("Inserisci denominatore"); double den = Convert.ToDouble(Console.ReadLine()); ElaboraDivisioneServiceReference.Divisione fattori = new ElaboraDivisioneServiceReference.Divisione(); fattori.numeratore = num; fattori.denominatore = den;

try { double risultato = client.elaboraDivisione(fattori); Console.WriteLine("Il risultato della divisione : " + risultato); } catch (FaultException<string> ex) { Console.WriteLine(ex.Detail); } client.Close(); Console.ReadLine(); } }

Notate che nella chiamata delloperazione del servizio, viene passato in input un oggetto Divisione, che contiene i due fattori specificati, e non un messaggio formattato secondo quanto definito nella definizione dei MessageContract; lo stesso avviene nella risposta ricevuta. Questo perch di default, quando aggiungiamo il riferimento al servizio nel client, WCF non formatta i parametri delle operazioni nei messaggi. Per farlo, bisogna indicarlo esplicitamente nella maschera di interfaccia selezionando la casella Always generate message contracts:

Il codice lato client cambia per tener conto della formattazione dei parametri nei messaggi (sono evidenziate le modifiche dovut allutilizzo dei MessageContract di input e output delloperazione):
class Program { static void Main(string[] args) { ElaboraDivisioneServiceReference.ElaborareDivisioneServiceClient client = new ElaboraDivisioneServiceReference.ElaborareDivisioneServiceClient(); Console.WriteLine("Inserisci numeratore"); double num = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("Inserisci denominatore"); double den = Convert.ToDouble(Console.ReadLine()); ElaboraDivisioneServiceReference.Divisione fattori = new ElaboraDivisioneServiceReference.Divisione(); fattori.numeratore = num; fattori.denominatore = den; ElaboraDivisioneServiceReference.RichiestaElaborazioneDivisioneRequest input = new ElaboraDivisioneServiceReference.RichiestaElaborazioneDivisioneRequest(); input.fattori = fattori; try {

ElaboraDivisioneServiceReference.RichiestaElaborazioneDivisioneResponse output = client.elaboraDivisione(input); double risultato = output.risultato; Console.WriteLine("Il risultato della divisione : " + risultato); } catch (FaultException<string> ex) { Console.WriteLine(ex.Detail); } client.Close(); Console.ReadLine(); } }

A questo punto abbiamo definito gli attori principali che partecipano al processo di comunicazione, Provider e Consumer, e possiamo testare la nostra applicazione. Click destro sulla Soluzione, scegliere Propriet e impostare Single startup project su MyClient:

Avviare quindi lapplicazione MyHost che ospita il servizio: vostro_Path_Progetti_VisualStudio\WcfServiceCalculator\MyHost\bin\Debug\MyHost.exe (Ricordarsi che lhost deve essere sempre avviato prima di eseguire il client, altrimenti sar generata uneccezione). Ora potete eseguire la Soluzione (Ctrl + F5) e testare lapplicazione in uno scenario normale e passando zero come denominatore per ottenere leccezione gestita:

Volpe Giancarlo (volpe.giancarlo83@gmail.com)