Condividi tramite


Introduzione di uno sviluppatore a Windows Communication Foundation 4

Aaron Skonnard, Pluralsight

Originale: novembre 2009

Aggiornato a RTM: aprile 2010

Panoramica

.NET 4 include alcune nuove funzionalità accattivanti e miglioramenti accolti nell'area di Windows Communication Foundation (WCF). Questi miglioramenti WCF si concentrano principalmente sulla semplificazione dell'esperienza di sviluppo, consentendo più scenari di comunicazione e fornendo un'integrazione avanzata con Windows Workflow Foundation (WF) rendendo i "servizi del flusso di lavoro" un cittadino di prima classe in avanti.

La buona notizia è la maggior parte delle modifiche WCF 4 incentrate sul rendere più semplici gli scenari comuni di oggi e rendere possibili nuovi scenari di comunicazione e stili di sviluppo. Di conseguenza, lo spostamento delle soluzioni WCF esistenti in .NET 4 sarà abbastanza semplice in termini di migrazione. È sufficiente decidere quali funzionalità WCF 4 si vuole sfruttare nelle soluzioni in avanti. Il resto di questo documento presenta ognuna delle nuove aree di funzionalità WCF 4 e illustra come funzionano.

Novità di WCF 4

WCF 4 include un'ampia gamma di funzionalità specifiche, ma la figura 1 descrive le principali aree di funzionalità che verranno incentrate su tutto il documento seguente. Queste aree di funzionalità riepilogano la maggior parte delle novità di WCF 4 e evidenziano le opportunità di primo livello offerte da questa versione di .NET Framework.

Figura 1: Aree delle funzionalità WCF 4

Area di funzionalità Descrizione

Configurazione semplificata

Semplificazione della sezione di configurazione WCF tramite il supporto per gli endpoint predefiniti, le configurazioni di associazione e comportamento. Queste modifiche consentono di ospitare servizi gratuiti di configurazione, semplificando notevolmente l'esperienza di sviluppo per gli scenari WCF più comuni.

Individuazione

Nuovo supporto del framework per i comportamenti di individuazione dei servizi ad hoc e gestiti, conformi al protocollo di WS-Discovery standard.

Servizio di routing

Nuovo supporto del framework per un servizio di routing configurabile che è possibile usare nelle soluzioni WCF. Fornisce funzionalità per il routing basato su contenuto, il bridging del protocollo e la gestione degli errori.

Miglioramenti REST

Miglioramenti a WCF WebHttp Services con alcune funzionalità e strumenti aggiuntivi che semplificano lo sviluppo del servizio REST.

Servizi flusso di lavoro

Supporto avanzato del framework per l'integrazione di WCF con WF per implementare servizi di flusso di lavoro dichiarativi a esecuzione prolungata. Questo nuovo modello di programmazione offre entrambi i framework migliori da offrire (WCF & WF).

Dopo aver esaminato queste aree principali di funzionalità, verranno illustrate brevemente alcune delle funzionalità WCF più avanzate di livello inferiore che sono incluse in .NET 4, incluse le funzionalità di risoluzione dei tipi migliorate, il supporto per le code con consumer concorrenti ("contesto di ricezione"), il supporto per i dati binari non elaborati tramite un codificatore di flusso di byte e il supporto per la traccia basata su ETW ad alte prestazioni.

Al termine, si noterà che WCF 4 diventa più semplice da usare e offre un supporto più integrato per alcuni degli scenari e degli stili di sviluppo più comuni di oggi.

Configurazione semplificata

Il modello di programmazione unificato offerto da WCF in 3.x è una benedizione e una maledizione. Semplifica la scrittura della logica del servizio per una varietà di scenari di comunicazione diversi, ma aumenta anche la complessità sul lato della configurazione delle cose perché fornisce molte diverse opzioni di comunicazione sottostanti che si è costretti a comprendere prima di iniziare.

La realtà è la configurazione WCF in genere diventa l'area più costosa di usare WCF in pratica oggi e gran parte di quella complessità si trova sul personale IT/operazioni che non sono impreparati a gestirlo.

Data questa realtà, quando si considera la complessità net dell'uso di WCF 3.x, si potrebbe ragionevolmente concludere che è più difficile usare rispetto al suo predecessore ASP.NET servizi Web (ASMX). Con ASMX è stato possibile definire un'operazione [WebMethod] e il runtime ha automaticamente fornito una configurazione predefinita per le comunicazioni sottostanti. Quando si passa a WCF 3.x, invece, gli sviluppatori devono conoscere abbastanza sulle varie opzioni di configurazione WCF per definire almeno un endpoint. E il numero scoraggiante di opzioni di configurazione spesso spaventa alcuni sviluppatori.

Per rendere l'esperienza WCF complessiva semplice come ASMX, WCF 4 include un nuovo modello di "configurazione predefinita" che rimuove completamente la necessità di qualsiasi configurazione WCF. Se non si fornisce alcuna configurazione WCF per un servizio specifico, il runtime WCF 4 configura automaticamente il servizio con alcuni endpoint standard e configurazioni predefinite di binding/comportamento. In questo modo è molto più semplice ottenere un servizio WCF in esecuzione, soprattutto per coloro che non hanno familiarità con le varie opzioni di configurazione WCF e sono felici di accettare le impostazioni predefinite, almeno per iniziare.

Endpoint predefiniti

Con WCF 3.x, se si tenta di ospitare un servizio senza endpoint configurati, l'istanza di ServiceHost genererà un'eccezione che informa che è necessario configurare almeno un endpoint. Con WCF 4, questo non è più il caso perché il runtime aggiunge automaticamente uno o più "endpoint predefiniti" per l'utente, rendendo quindi il servizio utilizzabile senza alcuna configurazione.

e funziona nel modo seguente: Quando l'applicazione host chiama Open nell'istanza di ServiceHost, compila la descrizione del servizio interno dal file di configurazione dell'applicazione insieme a qualsiasi elemento che l'applicazione host può aver configurato in modo esplicito e se il numero di endpoint configurati è ancora zero, chiama AddDefaultEndpoints, un nuovo metodo pubblico trovato nella classe ServiceHost. Questo metodo aggiunge uno o più endpoint alla descrizione del servizio in base agli indirizzi di base del servizio (negli scenari IIS, questo è l'indirizzo svc). Poiché il metodo è pubblico, è anche possibile chiamarlo direttamente negli scenari di hosting personalizzati.

Per essere precisi, l'implementazione di AddDefaultEndpoints aggiunge un endpoint predefinito per ogni indirizzo di base per ogni contratto di servizio implementato dal servizio. Ad esempio, se il servizio implementa due contratti di servizio e si configura l'host con un singolo indirizzo di base, AddDefaultEndpoints configura il servizio con due endpoint predefiniti (uno per ogni contratto di servizio). Tuttavia, se il servizio implementa due contratti di servizio e l'host è configurato con due indirizzi di base (uno per HTTP e uno per TCP), AddDefaultEndpoints configurerà il servizio con quattro endpoint predefiniti.

Esaminiamo un esempio completo per illustrare questo lavoro. Si supponga di avere i contratti di servizio WCF seguenti e l'implementazione del servizio seguente:

[ServiceContract]

interfaccia pubblica IHello

{

    [OperationContract]

    void SayHello(nome stringa);

}

[ServiceContract]

interfaccia pubblica IGoodbye

{

    [OperationContract]

    void SayGoodbye(nome stringa);

}

public class GreetingService : IHello, IGoodbye // implementa entrambi i contratti

{

    public void SayHello(nome stringa)

    {

        Console.WriteLine("Hello {0}", nome);

    }

    public void SayGoodbye(nome stringa)

    {

        Console.WriteLine("Addio {0}", nome);

    }

}

Con WCF 4, è ora possibile usare ServiceHost per ospitare il servizio GreetingService senza alcuna configurazione dell'applicazione. Quando si usa ServiceHost negli scenari di hosting personalizzati, è necessario specificare uno o più indirizzi di base da usare. Di seguito viene illustrato come ospitare GreetingService in un'applicazione console e, di nuovo, è possibile presupporre che non sia presente alcun file app.config associato a questo programma:

programma di classe

{

    static void Main(string[] args)

    {

        L'host è configurato con due indirizzi di base, uno per HTTP e uno per TCP

        Host ServiceHost = nuovo ServiceHost(typeof(GreetingService),

            new Uri("https://localhost:8080/greeting"),

            new Uri("net.tcp://localhost:8081/greeting"));

        Host. Open();

        foreach (ServiceEndpoint se nell'host. Description.Endpoints)

            Console.WriteLine("A: , B: , C{2}: {0}{1}",

                se. Indirizzo, se.Binding.Name, se.Contract.Name);

        Console.WriteLine("Premere <INVIO> per arrestare il servizio").

        Console.ReadLine();

        Host. Chiudi();

    }

}

In questo esempio viene configurato ServiceHost con due indirizzi di base: uno per HTTP e un altro per TCP. Quando si esegue questo programma, verranno visualizzati quattro endpoint stampati nella finestra della console, come illustrato nella figura 2. Si otterranno due per l'indirizzo di base HTTP, uno per contratto e due per l'indirizzo di base TCP, di nuovo uno per contratto. Questo è tutto fornito dietro le quinte dall'istanza di ServiceHost.

Figura 2: Endpoint predefiniti visualizzati nella finestra della console

Si noti come WCF sceglie di usare BasicHttpBinding per gli endpoint HTTP predefiniti e netTcpBinding per gli endpoint TCP predefiniti. Ti mostrerò come modificare queste impostazioni predefinite a breve.

Tenere presente che questo comportamento predefinito dell'endpoint viene avviato solo quando il servizio non è stato configurato con gli endpoint. Se si modifica l'applicazione console per configurare il servizio con almeno un endpoint, non verrà più visualizzato alcun endpoint predefinito nell'output. Per illustrare questa operazione, è sufficiente aggiungere la riga di codice seguente che chiama AddServiceEndpoint dopo aver costruito l'istanza di ServiceHost:

...

Host ServiceHost = nuovo ServiceHost(typeof(GreetingService),

    new Uri("https://localhost:8080/greeting"),

    new Uri("net.tcp://localhost:8081/greeting"));

Host. AddServiceEndpoint(typeof(IHello), nuovo WSHttpBinding(), "myendpoint");

...

Se si esegue l'applicazione console con questa riga di codice inserita, si noterà che nell'output viene visualizzato solo un singolo endpoint, ovvero quello configurato manualmente nel codice precedente (vedere la figura 3).

Figura 3: Output della console dopo la configurazione dell'host con un singolo endpoint

Tuttavia, è sempre possibile chiamare AddDefaultEndpoints se si vuole ancora aggiungere il set di endpoint predefiniti insieme al proprio. Nell'esempio di codice seguente viene illustrato come eseguire questa operazione:

...

Host ServiceHost = nuovo ServiceHost(typeof(GreetingService),

    new Uri("https://localhost:8080/greeting"),

    new Uri("net.tcp://localhost:8081/greeting"));

Host. AddServiceEndpoint(typeof(IHello), nuovo WSHttpBinding(), "myendpoint");

Host. AddDefaultEndpoints();

...

Se si esegue di nuovo l'applicazione console con questa modifica, verranno visualizzati cinque endpoint visualizzati nella finestra della console: quella configurata manualmente insieme ai quattro endpoint predefiniti (vedere la figura 4).

Figura 4: Output della console dopo aver chiamato manualmente AddDefaultEndpoints

Ora che si comprende l'algoritmo e la meccanica per l'aggiunta di endpoint predefiniti ai servizi in fase di esecuzione, la domanda successiva consiste nel decidere quale associazione usare per un determinato indirizzo basato su WCF?

Mapping del protocollo predefinito

La risposta a questa domanda è semplice. WCF definisce un mapping di protocollo predefinito tra schemi di protocollo di trasporto ,ad esempio http, net.tcp, net.pipe e così via, e le associazioni WCF predefinite. Il mapping del protocollo predefinito è disponibile nel file .NET 4 machine.config.comments e si presenta come segue:

<system.serviceModel>

   <protocolMapping>

      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="" />

      <add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration=""/>

      <add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration=""/>

      <add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration=""/>

   </protocolMapping>

   ...

È possibile eseguire l'override di questi mapping a livello di computer aggiungendo questa sezione a machine.config e modificando il mapping per ogni schema di protocollo. Oppure se si vuole eseguire l'override solo all'interno dell'ambito di un'applicazione, è possibile eseguire l'override di questa sezione all'interno del file di configurazione dell'applicazione/Web.

Ad esempio, se l'organizzazione è incentrata principalmente sulla creazione di servizi RESTful con WCF, potrebbe essere opportuno modificare l'associazione predefinita per lo schema di protocollo "http" in WebHttpBinding. Nell'esempio seguente viene illustrato come eseguire questa operazione all'interno di un file di configurazione dell'applicazione:

<configurazione>

  <system.serviceModel>

    <protocolMapping>

      <add scheme="http" binding="webHttpBinding"/>

    </protocolMapping>

  </system.serviceModel>

</Configurazione>

Ora, se si esegue nuovamente l'applicazione console illustrata in precedenza con questa app.config sul posto, i due endpoint basati su HTTP predefiniti mostrano ora che usano WebHttpBinding (vedere La figura 5).

Figura 5: Output della console dopo l'override del mapping predefinito del protocollo HTTP

Una volta che WCF determina quale associazione usare tramite la tabella di mapping del protocollo, usa la configurazione di associazione predefinita durante la configurazione dell'endpoint predefinito. Se non si è soddisfatti delle impostazioni predefinite di binding predefinite, è anche possibile eseguire l'override della configurazione predefinita per un'associazione specifica.

Configurazioni di associazione predefinite

Ogni associazione WCF include una configurazione predefinita usata a meno che non venga eseguito esplicitamente l'override dall'applicazione host per un determinato endpoint. Ogni istanza di associazione usata viene sempre fornita con le impostazioni predefinite predefinite, a meno che non si scelga di eseguire l'override applicando una configurazione di associazione esplicita.

In WCF 3.x, questa operazione viene eseguita definendo una configurazione di associazione denominata che è possibile applicare alle definizioni degli endpoint tramite l'attributo bindingConfiguration. La meccanica dell'operazione è complessa e soggetta a errori.  Il file di configurazione seguente mostra un esempio tipico:

<configurazione>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicWithMtom" messageEncoding="Mtom"/>

      </basicHttpBinding>

    </Associazioni>

    <services>

      <nome del servizio="GreetingService">

        <endpoint address="mtom" binding="basicHttpBinding"

                  bindingConfiguration="BasicWithMtom"

                  contract="IHello"/>

      </service>

    </Servizi>

  </system.serviceModel>

</Configurazione>

Nell'esempio precedente la configurazione dell'associazione "BasicWithMtom" esegue l'override delle impostazioni predefinite per BasicHttpBinding modificando la codifica dei messaggi in MTOM. Tuttavia, questa configurazione di associazione ha effetto solo quando la si applica a un endpoint specifico tramite l'attributo "bindingConfiguration", ovvero il passaggio che spesso elude gli sviluppatori e il personale operativo, causando problemi di configurazione.

Con WCF 4, è ora possibile definire configurazioni di associazione predefinite omettendo semplicemente il nome di configurazione dell'associazione durante la definizione della nuova configurazione. WCF userà quindi tale configurazione predefinita per tutti gli endpoint usando tale associazione che non dispone di una configurazione di associazione esplicita impostata su di essi.

Ad esempio, se si aggiunge il file di app.config seguente all'applicazione console illustrata in precedenza, i due endpoint HTTP predefiniti riceveranno questa configurazione basicHttpBinding predefinita, che abilita MTOM:

<configurazione>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <messaggio di associazioneEncoding="Mtom"/><-- si noti che non esiste alcun attributo name ->

      </basicHttpBinding>

    </Associazioni>

  </system.serviceModel>

</Configurazione>

Naturalmente, è anche possibile aggiungere queste configurazioni di associazione predefinite a machine.config se si desidera che vengano eseguite in tutti i servizi in esecuzione nel computer oppure è possibile definirle in base a un'applicazione in base all'applicazione aggiungendo le configurazioni di binding predefinite all'interno del file di configurazione dell'applicazione.

Questa funzionalità offre un meccanismo semplice per definire un set standard di impostazioni predefinite di binding che è possibile usare in tutti i servizi senza imporre le complessità delle configurazioni di binding ad altri sviluppatori o al personale IT/operations. Possono semplicemente scegliere l'associazione appropriata e assicurarsi che la configurazione predefinita appropriata venga fornita dall'ambiente di hosting.

Oltre alle configurazioni di associazione predefinite, l'altra cosa da considerare per i servizi e gli endpoint è ciò che deve essere la configurazione del comportamento predefinita.

Configurazioni di comportamento predefinite

WCF 4 consente inoltre di definire configurazioni di comportamento predefinite per i servizi e gli endpoint, che possono semplificare le operazioni quando si vuole condividere una configurazione predefinita standard in tutti i servizi o gli endpoint in esecuzione in un computer o all'interno di una soluzione.

In WCF 3.x è necessario definire configurazioni di comportamento denominate applicate in modo esplicito ai servizi e agli endpoint tramite l'attributo "behaviorConfiguration". Con WCF 4 è possibile definire configurazioni di comportamento predefinite omettendo il nome nella definizione di configurazione. Se si aggiungono questi comportamenti predefiniti a machine.config, verranno applicati a tutti i servizi o agli endpoint ospitati nel computer. Se li si aggiunge a app.config, verranno applicati solo nell'ambito dell'applicazione host. Ecco un esempio:

<configurazione>

  <system.serviceModel>

    <Comportamenti>

      <comportamentiServizio>

        <comportamento><-- notare che nessun attributo name -->

          <serviceMetadata httpGetEnabled="true"/>

        </Comportamento>

       </Servicebehaviors>

    </Comportamenti>

  </system.serviceModel>

</Configurazione>

In questo esempio vengono attivati i metadati del servizio per qualsiasi servizio che non è disponibile con una configurazione esplicita del comportamento. Se si aggiunge questa configurazione di comportamento predefinita al file app.config per l'applicazione console mostrata in precedenza e si esegue di nuovo l'applicazione, è possibile passare all'indirizzo HTTP di base per recuperare la pagina della Guida del servizio e la definizione WSDL del servizio (vedere la figura 6).

Figura 6: Esplorazione dei metadati del servizio abilitata dalla configurazione del comportamento predefinita

Un'altra nuova funzionalità in WCF 4 è che le configurazioni di comportamento supportano ora un modello di ereditarietà. Se un'applicazione definisce una configurazione del comportamento usando lo stesso nome di uno già definito in machine.config, la configurazione del comportamento specifico dell'applicazione verrà unita alla configurazione a livello di computer, aggiungendo eventuali comportamenti aggiuntivi alla configurazione del comportamento composito derivato.

Endpoint standard

Correlato agli endpoint predefiniti è un'altra nuova funzionalità WCF 4 nota come "endpoint standard". È possibile considerare un endpoint standard come una definizione di endpoint preconfigurata comune integrata nel framework WCF 4 che è possibile usare semplicemente. Gli endpoint standard definiscono una configurazione dell'endpoint "standard" che in genere non viene modificata, anche se è possibile se è necessario come si vedrà a breve.

La figura 7 descrive gli endpoint standard forniti con WCF 4. Queste forniscono definizioni di endpoint standard per alcuni degli scenari di comunicazione e funzionalità WCF 4 più comuni. Ad esempio, nel caso di un endpoint MEX, sarà sempre necessario specificare IMetadataExchange per il contratto di servizio e probabilmente scegliere HTTP. Invece di forzare sempre questa operazione manualmente, WCF fornisce una definizione di endpoint standard per lo scambio metdata denominato "mexEndpoint" facile da usare.

Figura 7: Endpoint standard in WCF 4

Nome endpoint standard Descrizione

mexEndpoint

Definisce un endpoint standard per MEX configurato con IMetadataExchange per il contratto di servizio, mexHttpBinding come associazione predefinita (è possibile modificarla) e un indirizzo vuoto.

dynamicEndpoint

Definisce un endpoint standard configurato per l'uso dell'individuazione WCF all'interno di un'applicazione client WCF. Quando si usa questo endpoint standard, non è necessario un indirizzo perché durante la prima chiamata, il client eseguirà una query per un endpoint di servizio corrispondente al contratto specificato e si connetterà automaticamente. Per impostazione predefinita, la query di individuazione viene inviata tramite UDP multicast, ma è possibile specificare l'associazione di individuazione e i criteri di ricerca da usare quando è necessario.

discoveryEndpoint

Definisce un endpoint standard preconfigurato per le operazioni di individuazione all'interno di un'applicazione client. L'utente deve specificare l'indirizzo e l'associazione quando si usa questo endpoint standard.

udpDiscoveryEndpoint

Definisce un endpoint standard preconfigurato per le operazioni di individuazione all'interno di un'applicazione client usando l'associazione UDP in un indirizzo multicast. Deriva da DiscoveryEndpoint.

announcementEndpoint

Definisce un endpoint standard preconfigurato per la funzionalità di annuncio dell'individuazione. L'utente deve specificare l'indirizzo e l'associazione quando si usa questo endpoint standard.

Udpannouncementendpoint

Definisce un endpoint standard preconfigurato per la funzionalità di annuncio su un'associazione UDP in un indirizzo multicast. Questo endpoint deriva da announcementEndpoint.

workflowControlEndpoint

Definisce un endpoint standard per il controllo dell'esecuzione di istanze del flusso di lavoro (creazione, esecuzione, sospensione, terminazione e così via).

webHttpEndpoint

Definisce un endpoint standard configurato con WebHttpBinding e WebHttpBehavior. Usare per esporre i servizi REST.

webScriptEndpoint

Definisce un endpoint standard configurato con WebHttpBinding e WebScriptEnablingBehavior. Usare per esporre i servizi Ajax.

È possibile sfruttare uno di questi endpoint standard nelle proprie configurazioni del servizio semplicemente facendo riferimento a tali endpoint in base al nome. L'elemento <endpoint> include ora un attributo "kind" che è possibile usare per specificare il nome di un endpoint standard. Ad esempio, l'esempio seguente configura GreetingService con un endpoint MEX sfruttando la definizione standard "mexEndpoint":

<configurazione>

  <system.serviceModel>

    <services>

      <service name="GreetingService">

        <endpoint kind="basicHttpBinding" contract="IHello"/>

        <endpoint kind="mexEndpoint" address="mex" />

      </service>

    </Servizi>

  </system.serviceModel>

</Configurazione>

Anche se gli endpoint standard consentono di evitare la maggior parte dei dettagli di configurazione (ad esempio, con mexEndpoint non è necessario specificare l'associazione o il contratto), potrebbero essere presenti momenti in cui si vuole usarli, ma è necessario configurare le definizioni di endpoint standard in modo leggermente diverso. 

Quando è necessario eseguire questa operazione, è possibile usare la <sezione standardEndpoints> ed eseguire l'override della configurazione dell'endpoint per l'endpoint standard. È quindi possibile fare riferimento a tale configurazione quando si definisce un nuovo <endpoint> tramite l'attributo endpointConfiguration, come illustrato di seguito:

<configurazione>

  <system.serviceModel>

    <services>

      <service name="GreetingService">

        <endpoint binding="basicHttpBinding" contract="IHello"/>

        <endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="D11"/>

      </service>

    </Servizi>

    <standardEndpoints>

      <Udpdiscoveryendpoint>

        <standardEndpoint name="D11" discoveryVersion="WSDiscovery11"/>

      </Udpdiscoveryendpoint>

    </standardEndpoints>

    <Comportamenti>

      <comportamentiServizio>

        <Comportamento>

          <serviceDiscovery/>

          <serviceMetadata httpGetEnabled="true"/>

        </Comportamento>

      </Servicebehaviors>

    </Comportamenti>

  </system.serviceModel>

</Configurazione>

In questo esempio viene modificata la versione di WS-Discovery predefinita per l'endpoint standard denominato "udpDiscoveryEndpoint" (si parlerà più brevemente dell'individuazione del servizio).

Semplificazione dell'hosting IIS/ASP.NET

Date queste nuove funzionalità per gli endpoint predefiniti, le configurazioni di associazione predefinite e le configurazioni di comportamento predefinite, l'hosting in IIS/ASP.NET diventa molto più semplice in WCF 4. ASP.NET gli sviluppatori che vengono usati per lavorare con i servizi ASMX possono ora definire servizi WCF semplici per natura.

Di fatto, vedere quanto semplice sia la definizione del servizio WCF seguente:

<-- HelloWorld.svc ->

<%@ ServiceHost Language="C#" Debug="true" Service="HelloWorldService

    CodeBehind="~/App_Code/HelloWorldService.cs" %>

[ServiceContract]

classe public HelloWorldService

{

    [OperationContract]

    stringa pubblica HelloWorld()

    {

        restituire "hello, world";

    }

}

Si tratta della forma più semplice di definizione del servizio WCF perché non viene usata una definizione di interfaccia separata per definire il contratto di servizio e tutto è definito in un unico file, HelloWorld.svc (nota: non si consiglia questo approccio, notando solo che è possibile disegnare un confronto con ASMX). Questo aspetto dovrebbe essere molto simile ai servizi ASMX tipici, la differenza principale è quella dei nomi di attributo usati nella classe del servizio ,ad esempio [WebService] e [WebMethod]. Ci sono sicuramente meno parti mobili.

Con le nuove funzionalità wcf 4 descritte nella sezione precedente, è ora possibile passare a HelloWorld.svc senza alcuna configurazione WCF aggiuntiva e la logica di attivazione WCF creerà l'istanza di ServiceHost in background e la configurerà con un singolo endpoint HTTP predefinito. Se è stato aggiunto un comportamento del servizio predefinito al file di machine.config che abilita i metadati del servizio, verrà visualizzata la pagina della Guida wcf e il collegamento alla definizione WSDL quando si passa a HelloWorld.svc (vedere la figura 8).

Figura 8: Pagina della Guida di HelloWorldService

Se il computer dei metadati del servizio non è stato abilitato, è possibile abilitarlo all'interno dell'applicazione Web aggiungendo la configurazione del comportamento predefinita seguente al file di web.config:

...

<system.serviceModel>

  <Comportamenti>

    <comportamentiServizio>

      <comportamento><-- notare che non esiste alcun attributo name->

         <serviceMetadata httpGetEnabled="true"/>

      </Comportamento>

    </Servicebehaviors>

  </Comportamenti>

</system.serviceModel>

...

È anche possibile modificare altre impostazioni predefinite seguendo le procedure descritte nelle sezioni precedenti. Ad esempio, è possibile modificare il mapping del protocollo predefinito, aggiungere configurazioni di associazione predefinite o configurazioni di comportamento predefinite aggiuntive. Se il servizio implementa più contratti di servizio, l'istanza di ServiceHost risultante verrà configurata con un endpoint HTTP per contratto.

Si supponga, ad esempio, di ospitare GreetingService (da una versione precedente) tramite il file con estensione svc illustrato di seguito:

<-- GreetingService.svc -->

<%@ServiceHost Service="GreetingService"%>

Data la definizione per GreetingService, la prima volta che si passa a GreetingService.svc, la logica di attivazione WCF creerà l'istanza di ServiceHost e aggiungerà due endpoint HTTP predefiniti per il tipo GreetingService (uno per ogni contratto di servizio). È possibile verificare questa operazione passando alla definizione WSDL e si troveranno due <elementi di porta> all'interno dell'elemento del <servizio> .

In generale, queste semplificazioni di configurazione WCF dovrebbero rendere molto più facile per gli sviluppatori ASP.NET ottenere servizi WCF in esecuzione e all'interno delle applicazioni Web e offre il caso più semplice molto più vicino agli sviluppatori di esperienze usati con ASP.NET servizi Web.

Attivazione senza file

Anche se i file con estensione svc semplificano l'esposizione dei servizi WCF, un approccio ancora più semplice consiste nel definire gli endpoint di attivazione virtuale all'interno di Web.config, rimuovendo così completamente la necessità di file con estensione svc.

In WCF 4 è possibile definire endpoint di attivazione del servizio virtuale mappati ai tipi di servizio in Web.config. In questo modo è possibile attivare i servizi WCF senza dover mantenere i file con estensione svc fisici (a.k.a. "attivazione senza file"). Nell'esempio seguente viene illustrato come configurare un endpoint di attivazione:

<configurazione>

  <system.serviceModel>

    <serviceHostingEnvironment>

      <serviceActivations>

        <add relativeAddress="Greeting.svc" service="GreetingService"/>

      </serviceActivations>

    </serviceHostingEnvironment>

  </system.serviceModel>

</Configurazione>

Con questo punto, è ora possibile attivare GreetingService usando un percorso relativo di "Greeting.svc" (relativo all'indirizzo di base dell'applicazione Web). Per illustrare questa operazione, ho creato un'applicazione IIS nel computer denominato "GreetingSite", che ho assegnato al pool di applicazioni "ASP.NET v4.0" e l'ho mappata alla directory del progetto GreetingService che contiene la web.config illustrata in precedenza. Ora posso semplicemente passare a https://localhost/GreetingSite/Greeting.svc senza avere effettivamente un file con estensione svc fisico su disco. La figura 9 mostra l'aspetto del browser.

Figura 9: Esempio di attivazione senza file

Individuazione

La prossima funzionalità WCF 4 principale che verrà descritta è l'individuazione dei servizi. In alcuni ambienti specializzati orientati ai servizi, sono disponibili servizi la cui posizione di runtime è dinamica e cambia costantemente. Si consideri, ad esempio, gli ambienti in cui diversi tipi di dispositivi abilitati al servizio si aggiungono costantemente e lasciano la rete come parte della soluzione aziendale complessiva. La gestione di questa realtà richiede ai client di individuare dinamicamente la posizione di runtime degli endpoint di servizio.

WS-Discovery è una specifica OASIS che definisce un protocollo basato su SOAP per individuare dinamicamente la posizione degli endpoint di servizio in fase di esecuzione. Il protocollo consente ai client di eseguire il probe per gli endpoint di servizio che corrispondono a determinati criteri per recuperare un elenco di candidati adatti. Un client può quindi scegliere un endpoint specifico dall'elenco individuato e usare l'indirizzo dell'endpoint di runtime corrente.

WS-Discovery definisce due modalità principali dell'operazione: modalità ad hoc e modalità gestita. In modalità ad hoc, i client probe per i servizi inviando messaggi multicast. Il framework fornisce il meccanismo multicast UDP per questa modalità ad hoc. I servizi che corrispondono al probe rispondono direttamente al client. Per ridurre al minimo la necessità di polling client, i servizi possono anche "annunciare" se stessi durante l'aggiunta o l'uscita dalla rete inviando un messaggio multicast ai client che potrebbero essere "in ascolto". L'individuazione ad hoc è limitata dal protocollo usato per i messaggi multicast, nel caso in cui udp solo i servizi in ascolto nella subnet locale potranno ricevere i messaggi.

Con l'individuazione del servizio gestito, è possibile fornire un proxy di individuazione nella rete che "gestisce" gli endpoint di servizio individuabili. I client parlano direttamente al proxy di individuazione per individuare i servizi in base ai criteri di probing. Il proxy di individuazione necessita di un repository di servizi che può corrispondere alla query. Il modo in cui il proxy viene popolato con queste informazioni è un dettaglio dell'implementazione. I proxy di individuazione possono essere facilmente connessi a un repository di servizi di exisiting, possono essere preconfigurato con un elenco di endpoint o un proxy di individuazione può anche ascoltare gli annunci per aggiornarne la cache. In modalità gestita, gli annunci possono essere unicast direttamente a un destinatario, potenzialmente da un proxy di individuazione.

Il framework .NET 4.0 fornisce le classi di base necessarie per implementare il proprio proxy di individuazione. Le classi di base astraggono i dettagli del protocollo di individuazione in modo che sia sufficiente concentrarsi sulla logica che si vuole che il proxy di individuazione contenga. Ad esempio, è necessario definire solo le operazioni eseguite dal proxy di individuazione in risposta a un messaggio probe, messaggi di annuncio e risoluzione dei messaggi.

WCF 4 offre un'implementazione completa del protocollo WS-Discovery e fornisce il supporto per le modalità di individuazione ad hoc e gestite. Di seguito verranno esaminate brevemente.

Individuazione dei servizi semplice

Il modo più semplice per abilitare l'individuazione dei servizi è la modalità ad hoc. WCF semplifica l'individuazione dei servizi all'interno delle applicazioni host del servizio fornendo alcuni endpoint di individuazione standard e un comportamento di individuazione dei servizi. Per configurare il servizio per l'individuazione, aggiungere semplicemente l'endpoint "udpDiscoveryEndpoint" standard e quindi abilitare il <comportamento di serviceDiscovery> nel servizio.

Ecco un esempio completo che illustra come eseguire questa operazione:

<configurazione>

    <system.serviceModel>

      <services>

        <service name="CalculatorService">

          <endpoint binding="wsHttpBinding" contract="ICalculatorService" />

          <-- aggiungere un endpoint di individuazione UDP standard->

          <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>

        </service>

      </Servizi>

      <Comportamenti>

        <comportamentiServizio>

          <Comportamento>

            <serviceDiscovery/><-- abilitare il comportamento di individuazione del servizio ->

          </Comportamento>

        </Servicebehaviors>

      </Comportamenti>

    </system.serviceModel>

</Configurazione>

A questo scopo, il servizio diventa individuabile tramite UDP nella subnet locale. I client possono quindi sfruttare WS-Discovery in fase di esecuzione per "individuare" l'indirizzo effettivo del servizio in esecuzione. WCF 4 semplifica l'esecuzione dei client tramite l'endpoint standard dynamicEndpoint.

È sufficiente prendere l'endpoint client esistente che si usa per connettersi al servizio, rimuovere l'indirizzo e aggiungere un tag kind="dynamicEndpoint".

<configurazione>

    <system.serviceModel>

        <client>

          <Endpoint

              name="calculatorEndpoint"

              kind="dynamicEndpoint"

              binding="wsHttpBinding"

              contract="ICalculatorService">

          </Endpoint>

        </Client>

    </system.serviceModel>

</Configurazione>

Quando viene effettuata la prima chiamata al servizio, il client invierà una query multicast che cerca servizi che corrispondono al contratto ICalculatorService e tenterà di connettersi a uno. Le varie impostazioni consentono di ottimizzare il serach, modificare le associazioni di individuazione e controllare il processo di individuazione. È anche possibile eseguire tutte queste operazioni a livello di codice usando la classe DiscoveryClient.

L'esempio seguente illustra ulteriormente come usare UDPDiscoveryEndpoint a livello di codice per individuare un endpoint ICalculatorService e quindi richiamarlo:

Creare DiscoveryClient

DiscoveryClient discoveryClient =

    new DiscoveryClient(new UdpDiscoveryEndpoint());

Trovare gli endpoint ICalculatorService nell'ambito specificato

FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));

FindResponse findResponse = discoveryClient.Find(findCriteria);

Selezionare il primo endpoint individuato

EndpointAddress address = findResponse.Endpoints[0]. Indirizzo;

Creare il client del servizio di destinazione

Client CalculatorServiceClient =

    new CalculatorServiceClient("calculatorEndpoint");

Connettersi all'endpoint del servizio individuato

Client. Endpoint.Address = indirizzo;

Console.WriteLine("Invoking CalculatorService at {0}", address);

Chiamare l'operazione Aggiungi servizio.

double result = client. Add(100, 15.99);

Console.WriteLine("Add({0},{1}) = {2}", 100, 15.99, risultato);

Dopo aver recuperato la raccolta di endpoint individuati, il programma client può usarlo per richiamare effettivamente il servizio di destinazione. La figura 10 mostra l'output dell'esecuzione del codice client illustrato sopra presupponendo che il servizio sia in esecuzione contemporaneamente. Nota: in questo esempio l'operazione Find nel client di individuazione è sincrona; l'individuazione offre anche il supporto per le operazioni di ricerca asincrone.

Figura 10: Output dell'esecuzione del codice client di individuazione

Uso degli ambiti durante l'individuazione degli endpoint

Nell'esempio precedente, il client ha semplicemente eseguito il probe per i servizi in base al tipo di contratto di servizio. I client possono limitare i risultati dell'individuazione fornendo informazioni aggiuntive sull'ambito durante l'invio dei probe di individuazione. Si esaminerà un semplice esempio per vedere come è possibile usare "ambiti" durante l'individuazione.

Prima di tutto, il servizio deve associare uno o più ambiti a ogni endpoint che verrà pubblicato per l'individuazione. WCF 4 include un <comportamento endpointDiscovery> che è possibile usare per definire un set di ambiti che è possibile associare a una definizione di endpoint. Nell'esempio seguente viene illustrato come associare due ambiti al singolo endpoint definito nel servizio:

<configurazione>

    <system.serviceModel>

      <services>

        <service name="CalculatorService"

                 behaviorConfiguration="calculatorServiceBehavior">

          <endpoint binding="wsHttpBinding"

                    contract="ICalculatorService"

                    behaviorConfiguration="ep1Behavior" />

          <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>

        </service>

      </Servizi>

      <Comportamenti>

        <comportamentiServizio>

          <nome comportamento="calculatorServiceBehavior">

            <serviceDiscovery/>

          </Comportamento>

        </Servicebehaviors>

        <comportamentiEndpoint>

          <nome comportamento="ep1Behavior">

            <endpointDiscovery>

               <-- ambiti associati a questo comportamento dell'endpoint ->

              <Ambiti>

                <add scope="http://www.example.org/calculator"/>

                <add scope="ldap:///ou=engineering,o=exampleorg,c=us"/>

              </Ambiti>

            </endpointDiscovery>

          </Comportamento>

         </endpointBehaviors>

      </Comportamenti>

    </system.serviceModel>

</Configurazione>

I client possono eseguire il probe per gli endpoint di servizio in base a ambiti specifici in fase di esecuzione. Possono farlo aggiungendo un elenco di ambiti di destinazione all'istanza di FindCriteria fornita all'operazione Find. Il codice seguente illustra come individuare gli endpoint ICalculatorService corrispondenti a un ambito LDAP specifico:

...

Creare DiscoveryClient

DiscoveryClient discoveryClient = new DiscoveryClient("udpDiscoveryEndpoint");

Trovare gli endpoint ICalculatorService nell'ambito specificato

Ambito URI = nuovo Uri("ldap:///ou=engineering,o=exampleorg,c=us");

FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));

findCriteria.Scopes.Add(scope);

FindResponse findResponse = discoveryClient.Find(findCriteria);

...

L'uso degli ambiti consente di ottimizzare l'implementazione dell'individuazione in modo che i client possano individuare più facilmente gli endpoint di servizio specifici di interesse. L'individuazione consente anche una maggiore personalizzazione. Ad esempio, i servizi possono aggiungere metadati XML personalizzati a un endpoint. Queste informazioni vengono inviate insieme al client in risposta alla query del client.

Annunci di servizio

WCF 4 semplifica anche la configurazione dei servizi per "annunciare" gli endpoint all'avvio. In questo modo, i client che sono "in ascolto" possono conoscere i nuovi endpoint di servizio direttamente durante l'aggiunta alla rete, riducendo così la quantità di messaggistica probing (e multicast) eseguiti dai client.

È possibile configurare un servizio con un endpoint di annuncio usando il <comportamento di serviceDiscovery> . Il <comportamento di serviceDiscovery> consente di definire una raccolta di endpoint di annuncio che verranno esposti dal servizio. È possibile usare lo standard "udpAnnouncementEndpoint" per la maggior parte dei casi.

È anche necessario configurare il servizio con uno standard "udpDiscoveryEndpoint" se si vuole rispondere ai probe di individuazione avviati dai client. Nell'esempio seguente viene illustrata una configurazione tipica:

<configurazione>

  <system.serviceModel>

    <services>

      <service name="CalculatorService">

        <endpoint binding="wsHttpBinding" contract="ICalculatorService"/>

        <endpoint kind="udpDiscoveryEndpoint"/>

      </service>

    </Servizi>

    <Comportamenti>

      <comportamentiServizio>

        <Comportamento>

          <serviceDiscovery>

            <announcementEndpoints>

              <endpoint kind="udpAnnouncementEndpoint"/>

            </announcementEndpoints>

          </serviceDiscovery>

        </Comportamento>

      </Servicebehaviors>

    </Comportamenti>

  </system.serviceModel>

</Configurazione>

Con questa configurazione, il servizio annuncia se stesso quando viene online e annuncia anche quando verrà offline. Per sfruttare questi annunci, è necessario progettare in modo specifico i client in ascolto in fase di esecuzione. A tale scopo, ospitare un servizio annunci all'interno dell'applicazione client che implementa il protocollo di annuncio WS-Discovery.

WCF 4 include una classe denominata AnnouncementService progettata in modo specifico per questo scopo. AnnouncementService fornisce due gestori eventi: OnlineAnnouncementReceived e OfflineAnnouncementReceived. Le applicazioni client possono semplicemente ospitare un'istanza di AnnouncementService usando ServiceHost e registrare i gestori eventi per questi due eventi.

Ogni volta che un servizio viene online e si annuncia, il client-hosted AnnouncementService riceverà l'annuncio "online" e OnlineAnnouncementReceived verrà generato nel client. Quando il servizio viene offline, invierà un annuncio "offline" e OfflineAnnouncementReceived verrà generato nel client. Di seguito viene illustrata un'applicazione client di esempio che ospita Il servizio Annunci e implementa i gestori per i due eventi di annuncio:

client di classe

{

    public static void Main()

    {

        Creare un'istanza di AnnouncementService

        AnnouncementService announcementService = new AnnouncementService();

        Sottoscrivere gli eventi di annuncio

        announcementService.OnlineAnnouncementReceived += OnOnlineEvent;

        announcementService.OfflineAnnouncementReceived += OnOfflineEvent;

        Creare ServiceHost per l'annuncioService

        using (ServiceHost announcementServiceHost =

            new ServiceHost(announcementService))

        {

            Ascoltare gli annunci inviati tramite il multicast UDP

            announcementServiceHost.AddServiceEndpoint(

                new UdpAnnouncementEndpoint());

            announcementServiceHost.Open();

            Console.WriteLine("Ascolto degli annunci di servizio".

                    Console.WriteLine();

            Console.WriteLine("Premere <INVIO> per terminare".);

            Console.ReadLine();

        }

    }

    static void OnOnlineEvent(mittente dell'oggetto, AnnouncementEventArgs e)

    {

                Console.WriteLine();

        Console.WriteLine("Ricevuto un annuncio online da {0}:",

            e.EndpointDiscoveryMetadata.Address);

        PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);

    }

    static void OnOfflineEvent(mittente dell'oggetto, AnnouncementEventArgs e)

    {

                Console.WriteLine();

        Console.WriteLine("Ricevuto un annuncio offline da {0}:",

            e.EndpointDiscoveryMetadata.Address);

        PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);

    }

    ...

}

Figura 11: Ascolto dei messaggi di annuncio di individuazione

Si supponga di eseguire questo programma client e lasciarlo in esecuzione per un po'. Successivamente vengono eseguite alcune istanze dell'applicazione host del servizio. Quando ogni avvio viene avviato, verrà visualizzato un messaggio di annuncio "online" nella finestra della console client. Quando si chiude ogni applicazione host del servizio, verrà visualizzato un messaggio di annuncio "offline" nella finestra della console client. La figura 11 mostra la finestra della console client risultante dopo aver eseguito le operazioni appena descritte.

Tenere presente che la modalità di individuazione ad hoc funziona solo in una subnet locale. Se si vuole usare WS-Discovery oltre i limiti della rete locale, è necessario passare alla modalità di individuazione gestita. WCF 4 offre supporto per la compilazione dei componenti di individuazione gestiti necessari.

Individuazione del servizio gestita

L'implementazione della modalità di individuazione gestita è un po' più coinvolta della modalità ad hoc perché richiede di implementare un servizio proxy di individuazione. Il servizio proxy di individuazione è il componente che tiene traccia di tutti gli endpoint di servizio disponibili. In questo esempio viene usata la funzionalità di annuncio per aggiornare il proxy di individuazione. Esistono molti altri modi per fornire un proxy di individuazione con le informazioni di individuazione pertinenti, ad esempio è possibile connettere un database esistente di endpoint e acquisire dati da questa posizione. Come implementare un servizio proxy di individuazione?

WCF 4 include una classe di base denominata DiscoveryProxy da cui è possibile derivare per implementare un servizio proxy di individuazione. La figura 12 mostra l'inizio di un'implementazione del servizio proxy di individuazione personalizzata. Gli esempi di .NET 4 SDK contengono un'implementazione completa di esempio per il riferimento. Dopo aver completato l'implementazione del servizio proxy di individuazione, è necessario ospitarla da qualche parte.

Figura 12: Implementazione di un servizio proxy di individuazione personalizzato

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,

    ConcurrencyMode = ConcurrencyMode.Multiple)]

public class MyDiscoveryProxy : DiscoveryProxyBase

{

    Repository per archiviare EndpointDiscoveryMetadata.

    Un database o un file flat può essere usato anche.

    <Dizionario EndpointAddress, EndpointDiscoveryMetadata> onlineServices;

    public MyDiscoveryProxy()

    {

        this.onlineServices =

            new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();

    }

    OnBeginOnlineAnnouncement viene chiamato quando un messaggio Hello viene ricevuto dal proxy

    override protetto IAsyncResult OnBeginOnlineAnnouncement(

        Messaggio DiscoveryMessageSequenceSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata, callback AsyncCallback, stato dell'oggetto)

    {

        Questo. AddOnlineService(endpointDiscoveryMetadata);

        restituire nuovo OnOnlineAnnouncementAsyncResult(callback, stato);

    }

    override protected void OnEndOnlineAnnouncement(IAsyncResult result)

    {

        OnOnlineAnnouncementAsyncResult.End(result);

    }

    OnBeginOfflineAnnouncement viene chiamato quando viene ricevuto un messaggio Bye dal proxy

    override protetto IAsyncResult OnBeginOfflineAnnouncement(

        Messaggio DiscoveryMessageSequenceSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata, callback AsyncCallback, stato dell'oggetto)

    {

        Questo. RemoveOnlineService(endpointDiscoveryMetadata);

        restituire nuovo OnOfflineAnnouncementAsyncResult(callback, stato);

    }

    override protected void OnEndOfflineAnnouncement(IAsyncResult result)

    {

        OnOfflineAnnouncementAsyncResult.End(result);

    }

    OnBeginFind viene chiamato quando viene ricevuto un messaggio di richiesta probe dal proxy

    override protetto IAsyncResult OnBeginFind(

        FindRequestContext findRequestContext, callback AsyncCallback, stato dell'oggetto)

    {

        Questo. MatchFromOnlineService(findRequestContext);

        restituire nuovo OnFindAsyncResult(callback, stato);

    }

    protected override void OnEndFind(IAsyncResult result)

    {

        OnFindAsyncResult.End(result);

    }

    ...

Per questo esempio, si ospiterà semplicemente il servizio MyDiscoveryProxy in un'applicazione console.  Configurerò l'host con due endpoint: un endpoint di individuazione e un endpoint di annuncio. Nell'esempio seguente viene illustrato come ospitare correttamente il servizio MyDiscoveryProxy con entrambi questi endpoint:

programma di classe

{

    public static void Main()

    {

        Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");

        Annuncio URIEndpointAddress =

            new Uri("net.tcp://localhost:9021/Announcement");

        ServiceHost proxyServiceHost = nuovo ServiceHost(nuovo MyDiscoveryProxy());

        DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(

            new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));

        discoveryEndpoint.IsSystemEndpoint = false;

        AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(

            new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));

        proxyServiceHost.AddServiceEndpoint(discoveryEndpoint);

        proxyServiceHost.AddServiceEndpoint(announcementEndpoint);

        proxyServiceHost.Open();

        Console.WriteLine("Servizio proxy avviato".);

                Console.WriteLine();

        Console.WriteLine("Premere <INVIO> per terminare il servizio".);

                Console.WriteLine();

        Console.ReadLine();

        proxyServiceHost.Close();

    }

}

Dopo aver creato un servizio proxy di individuazione in esecuzione, è possibile configurare i servizi per annunciarsi direttamente al servizio proxy di individuazione. Analogamente, è possibile configurare le applicazioni client per eseguire il probe diretto del servizio proxy di individuazione (non più messaggistica multicast).

Configurare il servizio per annunciarsi direttamente al servizio proxy di individuazione specificando l'indirizzo di annuncio del proxy di individuazione durante la creazione di AnnouncementEndpoint all'interno dell'applicazione host del servizio. L'esempio seguente illustra come eseguire questa operazione:

...

Uri baseAddress = new Uri("net.tcp://localhost:9002/CalculatorService/" +

    Guid.NewGuid(). ToString());

Annuncio URIEndpointAddress = new Uri("net.tcp://localhost:9021/Announcement");

ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService), baseAddress);

ServiceEndpoint netTcpEndpoint = serviceHost.AddServiceEndpoint(

    typeof(ICalculatorService), new NetTcpBinding(), string. Vuoto);

Creare un endpoint di annuncio che punta al servizio proxy ospitato

AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(

    new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));

ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior();

serviceDiscoveryBehavior.AnnouncementEndpoints.Add(announcementEndpoint);

serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);

serviceHost.Open();

...

È quindi possibile configurare le applicazioni client per comunicare direttamente con il servizio proxy di individuazione specificando l'indirizzo probe del proxy di individuazione durante la creazione di DiscoveryEndpoint all'interno dell'applicazione client. L'esempio seguente illustra un modo per eseguire questa operazione:

...

Creare un endpoint di individuazione che punti al servizio proxy.

Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");

DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(

    new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));

Creare DiscoveryClient usando l'individuazione creata in precedenzaEndpoint

DiscoveryClientClient = new DiscoveryClient(discoveryEndpoint);

Trovare gli endpoint ICalculatorService

FindResponse findResponse = discoveryClient.Find(

    new FindCriteria(typeof(ICalculatorService)));

...

Verrà ora illustrato un esempio completo. In primo luogo, eseguirò l'applicazione proxy di individuazione in modo che il servizio proxy di individuazione sia disponibile per l'uso. Verrà quindi eseguita un'istanza dell'applicazione host del servizio, che verrà annunciata con il proxy di individuazione. In questo caso, verrà visualizzato un messaggio stampato nella finestra della console dell'applicazione proxy di individuazione (vedere la figura 13). Ciò illustra che il servizio ha annunciato correttamente il proxy di individuazione e il proxy di individuazione ha salvato le informazioni sul nuovo endpoint del servizio "online". Ora, se si esegue il codice client illustrato in precedenza, eseguirà il probe diretto del proxy di individuazione e recupererà l'indirizzo endpoint del servizio di destinazione attualmente in esecuzione.

Figura 13: Output del servizio proxy di individuazione in fase di esecuzione

La bellezza dell'individuazione dei servizi gestiti è che funziona attraverso i limiti di rete (si basa sulle chiamate tradizionali al servizio) e riduce la necessità di messaggistica multicast all'interno della soluzione di individuazione. Inoltre, poiché i client passano attraverso un proxy di individuazione per cercare i servizi, i servizi stessi non devono essere operativi per poter essere individuati.

Utilizzo avanzato del proxy di individuazione

Il modello di programmazione WCF offre una notevole flessibilità nell'implementazione di un proxy di individuazione. La ricezione di annunci è un modo per popolare l'elenco dei servizi; tuttavia non è l'unico metodo. Ad esempio, se l'ambiente contiene già un repository di servizi, è possibile creare facilmente una facciata proxy di individuazione sopra tale archivio per rendere individuabile il repository in fase di esecuzione.

Un proxy di individuazione può essere configurato in modalità ad hoc o di gestione. Quando si opera in modalità gestita, i client comunicano direttamente con il proxy in modo unicast usando annunci, probe e risolve. Il proxy trasmette anche la risposta in modo unicast al mittente.

Se il funzionamento in modalità ad hoc un proxy può restare in ascolto dei messaggi di individuazione multicast e rispondere direttamente al mittente. In questa modalità ad hoc, è anche possibile configurare un proxy in modo specifico per eliminare i messaggi multicast. Ovvero, se un proxy riceve un messaggio multicast, informa il mittente della sua presenza e informa il mittente di indirizzare ulteriori query nel proxy, evitando così ulteriori messaggi multicast.

Per altre informazioni su questi scenari di individuazione avanzati, vedere l'articolo WS-Discovery Introduzione all'indirizzo http://www.oasis-open.org/committees/download.php/32184/WS-D-primer-wd-04.docx.

Servizio di routing

In alcuni ambienti orientati ai servizi, spesso è utile sfruttare i servizi di "routing" centralizzati che fungono da broker o gateway per i servizi aziendali effettivi sparsi nell'organizzazione. Questo separa i consumer dai servizi aziendali reali e consente di eseguire una varietà di diversi tipi di elaborazione intermedia all'interno del nodo di routing.

Ad esempio, alcuni ambienti usano il routing per implementare un limite di sicurezza centralizzato che tutti i messaggi in ingresso devono superare. Alcune usano tecniche di routing basate sul contenuto per determinare quale servizio di destinazione usare in base al contenuto di un determinato messaggio in arrivo. Altri usano il routing per implementare il bridging del protocollo, consentendo così ai consumer di usare un set di protocolli per comunicare mentre il router usa un set diverso di protocolli per comunicare con il servizio di destinazione. Non è raro usare anche il routing per varie tecniche di bilanciamento del carico o anche di controllo delle versioni del servizio.

Indipendentemente dal motivo, il modello di routing intermedio è un requisito comune per la creazione di soluzioni SOA su larga scala. In WCF 3.x non era disponibile il supporto ufficiale per il routing. Anche se il framework ha fornito le API necessarie per implementare i propri servizi di routing, è stato molto lavoro per farlo correttamente. Diversi articoli sono stati pubblicati in MSDN Magazine che illustrano come eseguire questa operazione.

Poiché il routing è un requisito comune in questi giorni, WCF 4 ora include un "servizio di routing" ufficiale nel framework che è possibile ospitare e configurare in soluzioni personalizzate.

Informazioni su RoutingService

WCF 4 include una nuova classe denominata RoutingService, che fornisce un'implementazione generica del routing WCF da usare all'interno delle applicazioni. RoutingService può gestire il routing dei messaggi su qualsiasi protocollo supportato da WCF usando diversi modelli di messaggistica, ad esempio unidirezionale, request-response e messaggistica duplex. Di seguito viene illustrata la definizione della classe RoutingService:

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any,

    InstanceContextMode = InstanceContextMode.PerSession,

    UseSynchronizationContext = false, ValidateMustUnderstand = false),

 AspNetCompatibilityRequirements(RequirementsMode =

    AspNetCompatibilityRequirementsMode.Allowed)]

classe public sealed RoutingService: i contratti // consentono modelli di comunicazione diversi

    ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter,

    IDuplexSessionRouter, IDisposable

{

    ... // implementazione omessa

}

Come si può notare, la classe RoutingService deriva da più contratti di servizio per supportare più modelli di messaggistica. Ogni contratto di servizio fornisce supporto per un modello di messaggistica diverso, incluso il supporto per le comunicazioni basate su sessione, se appropriato.

L'intero scopo di RoutingService è ricevere messaggi in arrivo dai consumer e "instradarli" a un servizio downstream appropriato. RouterService determina il servizio di destinazione da usare valutando ogni messaggio in arrivo rispetto a un set di filtri messaggi. Di conseguenza, in qualità di sviluppatore, si controlla il comportamento di routing definendo i filtri dei messaggi, in genere in un file di configurazione. I servizi di destinazione potrebbero risiedere nello stesso computer del RouterService, ma non devono, ma potrebbero anche essere distribuiti attraverso la rete e potrebbero richiedere un'ampia gamma di protocolli diversi.

Hosting di RoutingService

È possibile ospitare RoutingService nell'applicazione come qualsiasi altro servizio WCF. È sufficiente creare un'istanza di ServiceHost e specificare RoutingService per il tipo di servizio. Dopo aver chiamato Open nell'istanza di ServiceHost, RoutingService sarà pronto per "instradare" i messaggi, come illustrato di seguito:

using System;

utilizzando System.ServiceModel;

utilizzando System.ServiceModel.Routing;

public static void Main()

{

    Creare un ServiceHost per il tipo RoutingService.

    using (ServiceHost serviceHost =

        new ServiceHost(typeof(RoutingService)))

    {

        provare

        {

            serviceHost.Open();

            Console.WriteLine("Il servizio di routing è ora in esecuzione.");

            Console.WriteLine("Premere <INVIO> per terminare il router.");

            È ora possibile accedere al servizio.

            Console.ReadLine();

            serviceHost.Close();

        }

        catch (CommunicationException)

        {

            serviceHost.Abort();

        }

    }

}

È anche possibile configurare RoutingService come qualsiasi altro servizio ed è il percorso in cui si definiscono i filtri di routing. Prima di tutto, è necessario configurarlo con uno o più endpoint. Quando si definisce un endpoint di routing, si sceglie un'associazione WCF e uno dei contratti di servizio di routing implementati da RoutingService illustrato in precedenza ,ad esempio ISimplexDatagramRouter, IRequestReplyRouter e così via. È possibile esporre più di un endpoint in RoutingService se si desidera supportare più modelli di messaggistica o associazioni WCF.

L'esempio seguente illustra come configurare RoutingService con quattro endpoint di routing: due che usano BasicHttpBinding (unidirezionale e request-reply) e due che usano WSHttpBinding (unidirezionale e request-reply). Si noti che è come configurare qualsiasi altro servizio WCF:

<configurazione>

  <system.serviceModel>

    <services>

      <--ROUTING SERVICE -->

      <comportamento del servizioConfiguration="routingData"

          name="System.ServiceModel.Routing.RoutingService">

        <host>

          <baseAddresses>

            <add baseAddress="https://localhost:8000/routingservice/router"/>

          </baseAddresses>

        </Host>

        <!--

          Definire e configurare l'endpoint su cui si vuole che il router sia in ascolto e

          Il contratto che si vuole usare. I contratti forniti dal router sono:

          ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter e

          IDuplexSessionRouter.

         -->

        <endpoint address="oneway-basic"

                  binding="basicHttpBinding"

                  name="onewayEndpointBasic"

                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />

        <endpoint address="oneway-ws"

                  binding="wsHttpBinding"

                  name="onewayEndpointWS"

                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />

        <endpoint address="twoway-basic"

                  binding="basicHttpBinding"

                  name="reqReplyEndpointBasic"

                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />

        <endpoint address="twoway-ws"

                  binding="wsHttpBinding"

                  name="reqReplyEndpointWS"

                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />

      </service>

    </Servizi>

    ...

Le interfacce ISimplexDatagramRouter e IRequestReplyRouter definiscono definizioni generiche di contratto di servizio unidirezionale e request-reply che possono essere usate in combinazione con contratti di servizio specifici dell'azienda. Di seguito viene illustrato come sono state definite queste interfacce in WCF:

[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",

    SessionMode = SessionMode.Allowed)]

interfaccia pubblica ISimplexDatagramRouter

{

    [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]

    IAsyncResult BeginProcessMessage(Message, callback AsyncCallback,

        stato dell'oggetto);

    void EndProcessMessage(IAsyncResult result);

}

[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",

    SessionMode = SessionMode.Allowed)]

interfaccia pubblica IRequestReplyRouter

{

    [OperationContract(AsyncPattern = true, IsOneWay= false, Action = "*",

        ReplyAction = "*")]

    [GenericTransactionFlow(TransactionFlowOption.Allowed)]

    IAsyncResult BeginProcessRequest(Message message, callback AsyncCallback,

        stato dell'oggetto);

    Message EndProcessRequest(IAsyncResult result);

}

La configurazione dell'endpoint precedente espone gli endpoint di routing da usare per i consumer.  Le applicazioni client sceglieranno uno di questi endpoint da usare all'interno del codice client e indirizzano tutte le chiamate al servizio direttamente a RoutingService. Quando RoutingService riceve un messaggio tramite uno di questi endpoint, valuta i filtri dei messaggi di routing per determinare dove inoltrare il messaggio.

Configurazione di RoutingService con filtri dei messaggi

È possibile configurare RoutingService con filtri dei messaggi tramite codice o configurazione , come tutto il resto in WCF. WCF 4 fornisce un RoutingBehavior per la gestione dei filtri dei messaggi di routing. È prima necessario abilitare RoutingBehavior in RouterService e quindi specificare il nome della tabella di filtro da usare con questa particolare istanza di RoutingService:

<configurazione>

  <system.serviceModel>

    ...

    <Comportamenti>

      <comportamentiServizio>

        <behavior name="routingData">

          <serviceMetadata httpGetEnabled="True"/>

          <-- Definire il comportamento di routing e specificare il nome della tabella dei filtri ->

          <routing filterTableName="filterTable1" />

        </Comportamento>

      </Servicebehaviors>

    </Comportamenti>

    ...

Se si esamina l'esempio precedente in cui è stato configurato RoutingService con gli endpoint, si noterà che il comportamento "routingData" è stato applicato al servizio tramite l'attributo behaviorConfiguration. Successivamente, è necessario definire una tabella di filtro denominata "filterTable1".

Tuttavia, prima di poter definire una tabella di filtro, sono necessarie definizioni di endpoint per i servizi di destinazione a cui si intende eseguire il routing. Questi endpoint di destinazione vengono definiti all'interno della sezione di configurazione client> WCF <perché RoutingService è essenzialmente il "client" quando inoltra i messaggi a un servizio di destinazione. L'esempio seguente illustra come definire due endpoint di destinazione a cui è possibile indirizzare:

<configurazione>

    ...

    <-- Definire gli endpoint client con cui si vuole che il router comunichi.

         Queste sono le destinazioni a cui il router invierà messaggi. -->

    <client>

      <endpoint name="CalculatorService1"

       address="https://localhost:8000/servicemodelsamples/calcservice1"

       binding="wsHttpBinding" contract="*" />

      <endpoint name="CalculatorService2"

       address="https://localhost:8001/servicemodelsamples/calcservice2"

       binding="wsHttpBinding" contract="*" />

    </Client>

    ...

Ora è possibile definire la tabella di filtro effettiva, che determinerà la logica di routing in fase di esecuzione. Definire le voci della tabella di filtro all'interno dell'elemento <filterTables> . Ogni voce all'interno di <filterTable> definisce un mapping tra un routing "filter" e un endpoint di destinazione. Si definiscono i "filtri" che si desidera usare all'interno dell'elemento <filtri> : ogni <voce di filtro specifica il tipo di filtro> che si desidera usare insieme ai dati specifici del filtro ,ad esempio un valore di azione, un'espressione XPath e così via.

Nell'esempio seguente viene illustrato come configurare una tabella di filtro con un singolo filtro che esegue il mapping all'endpoint CalculatorService1. In questo caso, il filtro "MatchAll" corrisponde a tutti i messaggi in ingresso:

<configurazione>

    ...

    <sezione --ROUTING -->

    <Routing>

      <-- Definire i filtri da usare per il router. -->

      <filters>

        <filter name="MatchAllFilter1" filterType="MatchAll" />

      </Filtri>

      <-- Definire la tabella di filtro contenente il filtro MatchAll ->

      <filterTables>

        <filterTable name="filterTable1">

            <-- Eseguire il mapping del filtro a un endpoint client definito in precedenza.

                 I messaggi che corrispondono a questo filtro verranno inviati a questa destinazione. -->

          <add filterName="MatchAllFilter1" endpointName="CalculatorService1" />

        </filterTable>

      </filterTables>

    </Routing>

  </system.serviceModel>

</Configurazione>

È possibile verificare che il routing funzioni correttamente eseguendo l'applicazione host del servizio di routing, l'applicazione host CalculatorService1 e un client progettato per inviare messaggi a uno degli endpoint del router. Quando si esegue il client, i messaggi arrivano a CalculatorService 1 dopo che vengono "instradati" dal routing intermedio (vedere la figura 14, la figura 15 e la figura 16).

Figura 14: Applicazione host RoutingService

Figura 15: Client destinato a RoutingService in https://localhost:8000/routingservice/router

Figura 16: Servizio di destinazione (CalculatorService1)

Filtri dei messaggi e routing basato su contenuto

WCF include diverse classi MessageFilter predefinite che è possibile usare insieme ai filtri dei messaggi di routing per controllare il contenuto dei messaggi in ingresso.

WCF, ad esempio, fornisce un ActionMessageFilter che consente di corrispondere a valori specifici WS-Addressing "action". WCF fornisce anche EndpointAddressMessageFilter, EndpointNameMessageFilter e PrefixEndpointAddressMessageFilter che consentono di corrispondere ai dettagli specifici dell'endpoint. Uno dei più flessibili è XPathMessageFilter, che consente di valutare le espressioni XPath rispetto ai messaggi in ingresso. Tutti questi filtri consentono di eseguire il routing basato sul contenuto all'interno della soluzione.

Oltre a questi tipi di MessageFilter predefiniti, WCF 4 consente anche di definire filtri di messaggi personalizzati, che si verifica come uno dei punti di estendibilità primari per RoutingService.

Si esaminerà un esempio di esecuzione del routing basato sul contenuto in base ai valori di azione. Si supponga di voler instradare la metà delle operazioni CalculatorService1 a CalculatorService1 e all'altra metà a CalculatorService2. A tale scopo, è possibile definire i filtri per ognuno dei diversi valori di azione CalculatorService e eseguire il mapping di metà di essi a ogni endpoint del servizio di destinazione, come illustrato di seguito:

<configurazione>

    ...

    <sezione --ROUTING -->

    <Routing>

      <-- Definire i filtri da usare per il router. -->

     <filters>

        <filter name="addFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Add"/>

         <filter name="subFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Subtract"/>

        <filter name="mulFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Multiply"/>

        <filter name="divFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Divide"/>

      </Filtri>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"/>

          <add filterName="subFilter" endpointName="CalculatorService2"/>

          <add filterName="mulFilter" endpointName="CalculatorService1"/>

          <add filterName="divFilter" endpointName="CalculatorService2"/>

        </filterTable>

      </filterTables>

    </Routing>

  </system.serviceModel>

</Configurazione>

Ora quando si esegue la soluzione ed si esegue il client che richiama tutte e quattro le operazioni, verrà visualizzata la metà delle operazioni in ogni finestra della console del servizio (vedere la figura 17 e la figura 18).

Figura 17: Output di CalculatorService1

Figura 18: Output di CalculatorService2

XPathMessageFilter offre una maggiore flessibilità perché è possibile fornire un'ampia gamma di espressioni XPath diverse da valutare rispetto ai messaggi in ingresso. Le espressioni XPath possono valutare qualsiasi parte del messaggio in ingresso, incluse le intestazioni SOAP o il corpo SOAP. Ciò offre una grande flessibilità durante la creazione di filtri dei messaggi basati su contenuto. Per comprendere i meccanismi di XPathMessageFilter, viene illustrato come riscrivere l'ultimo esempio usando espressioni XPath:

<configurazione>

    ...

    <sezione --ROUTING -->

    <Routing>

      <-- Definire i filtri da usare per il router. -->

     <filters>

        <filter name="addFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Add'"/>

        <filter name="subFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Subtract'"/>

        <filter name="mulFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Multiply'"/>

        <filter name="divFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Divide'"/>

      </Filtri>

      <namespaceTable>

        <add prefix="s" namespace="http://www.w3.org/2003/05/soap-envelope" />

        <add prefix="wsa" namespace="http://www.w3.org/2005/08/addressing" />

      </namespaceTable>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"/>

          <add filterName="subFilter" endpointName="CalculatorService2"/>

           <add filterName="mulFilter" endpointName="CalculatorService1"/>

          <add filterName="divFilter" endpointName="CalculatorService2"/>

        </filterTable>

      </filterTables>

    </Routing>

  </system.serviceModel>

</Configurazione>

Si noti che l'attributo filterData contiene un'espressione XPath che verrà valutata rispetto al messaggio in arrivo( le espressioni controllano semplicemente i valori dell'azione in questo esempio). Si noti che è stato definito anche un set di associazioni di prefisso dello spazio dei nomi usando l'elemento <namespaceTable> . Questa operazione è necessaria se si desidera usare i prefissi dello spazio dei nomi all'interno delle espressioni XPath come ho fatto in precedenza. La ripetizione dell'esecuzione della soluzione con questa configurazione produce gli stessi risultati di prima (vedere la figura 17 e la figura 18).

È necessario usare questa tecnica di filtro XPath ogni volta che è necessario instradare i messaggi in base alle intestazioni SOAP personalizzate o in base al contenuto trovato all'interno del corpo del messaggio SOAP.

Bridging del protocollo

Negli esempi precedenti si usava la stessa associazione WCF (WSHttpBinding) per comunicare tra il client e il router e tra il router e i servizi di destinazione. RoutingService è in grado di colmare la comunicazione tra la maggior parte delle associazioni WCF. Ad esempio, è possibile configurare il router in modo che i client comunichino con esso tramite WSHttpBinding, ma il router comunica con i servizi di destinazione downstream usando NetTcpBinding o NetNamedPipeBinding.

Verrà ora illustrato come configurare RoutingService per gestire questo scenario. Si lascerà invariata la configurazione dell'endpoint RoutingService, che consente ai consumer di comunicare con RoutingService tramite BasicHttpBinding o WSHttpBinding. Ma ora verranno modificate le definizioni degli endpoint client per i servizi di destinazione per usare NetTcpBinding e NetNamedPipeBinding, come illustrato di seguito:

<configurazione>

    ...

    <-- Definire gli endpoint client con cui si vuole che il router comunichi.

         Queste sono le destinazioni a cui il router invierà messaggi. -->

    <client>

      <endpoint name="CalculatorService1"

       address="net.tcp://localhost:8001/servicemodelsamples/calcservice1"

       binding="netTcpBinding" contract="*" />

      <endpoint name="CalculatorService2"

       address="net.pipe://localhost/servicemodelsamples/calcservice2"

       binding="netNamedPipeBinding" contract="*" />

    </Client>

    ...

E, naturalmente, sarà necessario aggiornare le applicazioni CalculatorService1 e CalculatorService2 per supportare rispettivamente gli endpoint NetTcpBinding e NetNamedPipeBinding compatibili. Con questa configurazione, i consumer possono comunicare con RoutingService usando BasicHttpBinding/WSHttpBinding e il router comunicherà con i servizi di destinazione usando NetTcpBinding o NetNamedPipeBinding a seconda del servizio a cui viene instradato il messaggio.

Tolleranza di errore e tolleranza di errore

RoutingService fornisce anche un meccanismo predefinito per gestire gli errori di comunicazione di runtime e supportare un livello di tolleranza di errore di base. Quando si definisce la tabella dei filtri, è possibile definire elenchi diversi di endpoint alternativi che verranno usati da RoutingService se la comunicazione con l'endpoint di destinazione iniziale genera un errore. Ciò consente essenzialmente di avere elenchi di endpoint di "backup".

Nell'esempio seguente viene illustrato come definire un elenco di endpoint di backup all'interno dell'elemento <backupLists> che è possibile associare alle voci della tabella di filtro:

<configurazione>

    ...

    <sezione --ROUTING -->

    <Routing>

      ... <! - Definire i filtri da usare per il router. -->

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="subFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="mulFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="divFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

        </filterTable>

      </filterTables>

      <backupLists>

        <backupList name="backupEndpoints">

          <add endpointName="CalculatorService2"/>

        </backupList>

      </backupLists>

    </Routing>

  </system.serviceModel>

</Configurazione>

Si noti come tutte le voci della tabella di filtro siano configurate per l'inoltro a CalculatorService1 in questo esempio, poiché CalculatorService2 è ora l'endpoint di "backup" che verrà usato solo quando CalculatorService1 genera un'eccezione TimeoutException, una CommunicationException o un tipo di eccezione derivata. Ad esempio, se si esegue nuovamente la soluzione e si chiude CalculatorService1 e si esegue il programma client, verranno visualizzati tutti i messaggi in CalculatorService2. È importante notare, ancora una volta, che tutta questa configurazione di routing può essere eseguita dinamicamente nel codice dell'applicazione host.

Comportamento di routing multicast

RoutingService supporta anche il routing automatico di un determinato messaggio in ingresso a più destinazioni in modo "multicast". Quando il messaggio in arrivo corrisponde a più filtri trovati nella tabella di filtro configurata, RoutingService instrada automaticamente il messaggio a ognuno degli endpoint di destinazione associati ai filtri "corrispondenti".

L'esempio seguente mostra due voci di routing configurate con lo stesso filtro con caratteri jolly, che corrispondono a tutti i messaggi in arrivo:

<configurazione>

    ...

    <sezione --ROUTING -->

    <Routing>

      <-- Definire i filtri da usare per il router. -->

     <filters>

        <filter name="wildcardFilter" filterType="MatchAll" />

      </Filtri>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="wildcardFilter" endpointName="CalculatorService1"/>

          <add filterName="wildcardFilter" endpointName="CalculatorService2"/>

          <add filterName="wildcardFilter" endpointName="CalculatorService3"/>

        </filterTable>

      </filterTables>

    </Routing>

  </system.serviceModel>

</Configurazione>

Con questa configurazione, ogni messaggio SOAP in ingresso verrà instradato automaticamente a tutti gli endpoint di destinazione, indipendentemente dagli elementi trovati nei messaggi.

Questo comportamento multicast si compone con le funzionalità di bridging del protocollo e di gestione degli errori descritte nelle sezioni precedenti. L'unico problema è che il multicast funziona solo per la comunicazione unidirezionale o duplex, ma non per la comunicazione request-response, poiché il sistema sottostante deve mantenere un rapporto uno-a-uno tra le richieste in uscita e le risposte in ingresso.

Supporto REST migliorato

WCF 4 include diverse nuove funzionalità utili per la creazione di servizi RESTful tramite WCF.  Questo set di funzionalità è ora definito WebHttp Services WCF. Includono il supporto per una pagina della Guida automatica che descrive il servizio RESTful ai consumer, la memorizzazione nella cache HTTP semplificata, la selezione del formato dei messaggi, le eccezioni compatibili con REST, l'integrazione del routing ASP.NET, alcuni nuovi modelli di progetto di Visual Studio e altro ancora. Non avremo spazio per coprire tutte queste funzionalità qui in dettaglio, ma vi darò una rapida introduzione a alcuni dei miei preferiti di seguito, insieme a collegamenti a altre informazioni sul resto.

Molte di queste funzionalità sono state introdotte per la prima volta dallo Starter Kit REST WCF l'anno scorso e ora lo rendono nel framework ufficiale. In futuro potrebbero essere visualizzate altre funzionalità di WCF REST Starter Kit.

Pagina guida automatica

Quando si usa la classe WebServiceHost in WCF 4, i servizi RESTful usufruiranno automaticamente dei vantaggi della funzionalità della pagina della Guida automatica. Questa è un'aggiunta molto necessaria quando si usa REST data la mancanza di metadati WSDL e la generazione di codice lato client, che rende molto più semplice per gli utenti capire come iniziare a usare il servizio, quindi in genere è consigliabile abilitare questa nuova funzionalità.

Quando si usa la classe WebServiceHost per ospitare il servizio, il servizio viene configurato automaticamente con WebHttpBehavior e viene aggiunto un endpoint HTTP predefinito configurato con WebHttpBinding (all'indirizzo HTTP di base). A partire da WCF 4, la classe WebHttpBehavior include una proprietà HelpEnabled che controlla se la nuova pagina della Guida è abilitata all'interno dell'host. L'esempio di configurazione seguente illustra come abilitare la funzionalità della pagina della Guida automatica per un endpoint REST specifico:

<configurazione>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

    <Comportamenti>

      <comportamentiEndpoint>

        <nome comportamento="HelpBehavior">

          <webHttp helpEnabled="true" />

        </Comportamento>

      </endpointBehaviors>

    </Comportamenti>

    <services>

      <service name="CounterResource">

        <endpoint behaviorConfiguration="HelpBehavior"

                  binding="webHttpBinding"

                  contract="CounterResource" />

      </service>

    </Servizi>

  </system.serviceModel>

</Configurazione>

È possibile visualizzare la pagina della Guida passando all'indirizzo di base del servizio con "help" accodato alla fine dell'URL (vedere la figura 19).

Figura 19: Pagina guida automatica per i servizi RESTFul

La pagina della Guida fornisce una descrizione leggibile di ogni operazione annotata con [WebGet] o [WebInvoke], e per ognuna descrive il modello URI, l'operazione HTTP supportata e i formati di messaggio supportati, fondamentalmente tutto ciò che un consumer deve conoscere (vedere La figura 20). È anche possibile fornire una descrizione più descrittiva dell'utente applicando un attributo [Description] a ogni operazione.

Per ogni richiesta/risposta, la pagina della Guida fornisce anche un XML Schema e un'istanza XML di esempio corrispondente che i consumer possono usare per integrare con il servizio. I consumer possono usare lo schema per generare tipi serializzabili sul lato client appropriati oppure possono semplicemente esaminare il documento XML di esempio per determinare manualmente come scrivere il codice di elaborazione XML appropriato. Entrambi gli approcci sono utili.

Figura 20: Pagina guida automatica per un'operazione specifica

Questa nuova funzionalità della pagina della Guida rende automaticamente individuabili i servizi RESTful, che in definitiva semplificano l'utilizzo di altri utenti. I consumer possono individuare la progettazione dell'URI del servizio, le operazioni HTTP supportate e i formati di richiesta/risposta e la descrizione rimarrà sempre sincronizzata con il codice WCF, analogamente a quanto funzionano con i servizi Web ASP.NET.

Supporto per la memorizzazione nella cache HTTP

Uno dei potenziali vantaggi principali di REST è la memorizzazione nella cache HTTP. Tuttavia, per realizzare tale vantaggio, è necessario sfruttare le varie intestazioni di memorizzazione nella cache HTTP nei messaggi di richiesta e risposta. È possibile eseguire questa operazione all'interno delle operazioni del servizio WCF accedendo manualmente alle intestazioni di richiesta/risposta tramite l'istanza webOperationContext, ma non è semplice eseguire correttamente.

Di conseguenza, WCF 4 include un modello più semplice per controllare la memorizzazione nella cache tramite l'attributo [AspNetCacheProfile] che è possibile applicare in modo dichiarativo alle operazioni GET. Questo attributo consente di specificare un nome del profilo di memorizzazione nella cache ASP.NET per ogni operazione e dietro le quinte è presente un controllo cache (CachingParameterInspector) che si occupa della gestione di tutti i dettagli della memorizzazione nella cache HTTP sottostante.

L'implementazione [AspNetCacheProfile] si basa sul meccanismo di memorizzazione nella cache degli output standard ASP.NET. Nell'esempio seguente viene illustrato come applicare l'attributo [AspNetCacheProfile] a un'operazione [WebGet]:

...

[AspNetCacheProfile("CacheFor60Seconds")]

[WebGet(UriTemplate=XmlItemTemplate)]

[OperationContract]

public Counter GetItemInXml()

{

    return HandleGet();

}

...

Con questo risultato, è necessario definire un profilo di memorizzazione nella cache di output ASP.NET denominato "CacheFor60Seconds" all'interno del file di web.config. La web.config seguente illustra come eseguire questa operazione:

<configurazione>

  <system.web>

    <Memorizzazione nella cache>

      <outputCacheSettings>

        <outputCacheProfiles>

          <add name="CacheFor60Seconds" duration="60" varyByParam="format" />

        </outputCacheProfiles>

      </outputCacheSettings>

    </Memorizzazione nella cache>

  </system.web>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

  </system.serviceModel>

</Configurazione>

Si noti come il profilo di memorizzazione nella cache ASP.NET sia impostato per memorizzare nella cache l'output per 60 secondi e sia configurato per variare la cache in base alla variabile di stringa di query "format". Questo è importante perché il servizio in questione supporta sia XML che JSON, che si controlla tramite la variabile di formato ,ad esempio "?format=json"). Il servizio deve memorizzare nella cache le risposte XML e JSON in modo indipendente tra loro.

Questo file di configurazione illustra anche come abilitare la modalità di compatibilità ASP.NET, necessaria quando si desidera sfruttare varie funzionalità ASP.NET come la memorizzazione nella cache dell'output.

L'attributo [AspNetCacheProfile] rende molto più semplice sfruttare la memorizzazione nella cache HTTP senza richiedere l'uso diretto delle intestazioni di memorizzazione nella cache HTTP. Il comportamento WCF sottostante si occupa dell'inserimento della cache HTTP, della data, della scadenza e delle intestazioni variano HTTP nella risposta, che i client possono anche sfruttare per determinare la semantica di memorizzazione nella cache appropriata in prossimità di round trip futuri.

Selezione formato messaggio

Se si esamina la figura 19, si noterà che il servizio CounterResource supporta i formati XML e JSON per le operazioni GET, POST e PUT. Ciò è stato possibile dal momento che WCF 3.5 tramite le proprietà RequestFormat e ResponseFormat trovate negli attributi [WebGet] e [WebInvoke] .

Questa operazione è stata eseguita nel servizio CounterResource definendo un contratto di operazione separato per ogni versione, una per la versione XML e un'altra per la versione JSON, come illustrato di seguito:

...

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItemInXml()

{

    return HandleGet();

}

[WebGet(UriTemplate = "?format=json", ResponseFormat=WebMessageFormat.Json)]

[OperationContract]

public Counter GetItemInJson()

{

    return HandleGet();

}

...

Ciò significa purtroppo che per ogni operazione logica sono necessari due contratti di operazione se si desidera supportare sia i formati XML che JSON. Nel caso del servizio CounterResource, è stato necessario avere sei contratti di operazione per supportare sia XML che JSON per le operazioni GET, POST e PUT.

WCF 4 rende questo scenario molto più semplice da gestire fornendo supporto per la selezione automatica dei formati in base alle intestazioni HTTP "Accept", che è un modo migliore per farlo. Lasciatemi spiegare come funziona questa operazione.

Prima di tutto, è necessario un singolo contratto di operazione per ogni operazione logica:

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItem()

{

    return HandleGet();

}

Quindi la selezione automatica del formato nel web standardHttpEndpoint come illustrato di seguito:

<configurazione>

  <system.serviceModel>

    <standardEndpoints>

      <webHttpEndpoint>

        <-- l'endpoint standard "" viene usato per la creazione automatica di un endpoint Web. -->

        <standardEndpoint name="" helpEnabled="true"

            automaticFormatSelectionEnabled="true"/>

      </webHttpEndpoint>

    </standardEndpoints>

  </system.serviceModel>

</Configurazione>

Con questo risultato, il servizio è ora in grado di gestire e restituire messaggi XML o JSON. Viene illustrato quale formato usare controllando prima l'intestazione HTTP Accept trovata nel messaggio di richiesta. In caso contrario, userà lo stesso formato di messaggio del messaggio del messaggio di richiesta, presupponendo che sia XML o JSON, altrimenti userà il formato predefinito per l'operazione specifica.

È ora necessario che il client determini quale formato usare tramite le intestazioni Tipo di contenuto HTTP e Accetta. L'intestazione Content-Type specifica il formato nel messaggio di richiesta mentre l'intestazione Accept indica il formato del client "accetta" dal servizio. Ad esempio, se il client vuole ricevere json dal servizio, deve specificare l'intestazione HTTP Accept seguente nella richiesta:

Accept: application/json

Questo è facile da eseguire in qualsiasi modello di programmazione client HTTP ed è anche facile usare strumenti come Fiddler. La figura 21 illustra come usare Fiddler per recuperare JSON dal servizio nuovo e migliorato.

Figura 21: Uso dell'intestazione ACCETTA HTTP per recuperare JSON

WCF 4 fornisce anche nuove API che semplificano l'impostazione esplicita del formato del messaggio in fase di esecuzione. Il codice seguente illustra come estendere l'operazione GetItem scrivendo il codice che cerca innanzitutto un parametro stringa di query "format" e imposta in modo esplicito il formato di risposta di conseguenza. Se non trova un parametro "format", si basa semplicemente sull'intestazione Accept come prima.

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItem()

{

    se è stato specificato un parametro stringa di query di formato,

    impostare il formato di risposta su questo. In caso contrario

    parametro della stringa di query esiste l'intestazione Accept

    formato stringaQueryStringValue =

       WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[

       "format"];

    se (!string). IsNullOrEmpty(formatQueryStringValue))

    {

        if (formatQueryStringValue.Equals("xml",

           System.StringComparison.OrdinalIgnoreCase))

        {

            WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

        }

        else if (formatQueryStringValue.Equals("json",

           System.StringComparison.OrdinalIgnoreCase))

        {

            WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

        }

        else

        {

            genera una nuova stringa WebFaultException<(string>). Format("Formato non supportato '{0}'",

               formatQueryStringValue), HttpStatusCode.BadRequest);

        }

    }

    return HandleGet();

}

Alla fine, queste nuove funzionalità lo rendono in modo che non sia più necessario hard-code un tipo di formato di messaggio nel tuo [WebGet] e [WebInvoke] contratti di operazione come hai fatto in WCF 3.5.

Gestione degli errori RESTful

Poiché i servizi RESTful non usano SOAP, non è più disponibile il meccanismo di errore SOAP standard, che consente di eseguire automaticamente il mapping tra eccezioni .NET e messaggi di errore SOAP. Quando si creano servizi REST in WCF 3.5, è necessario costruire manualmente il messaggio di risposta HTTP ogni volta che si vuole personalizzare il messaggio di errore HTTP inviato al client.

In WCF 4 è disponibile una nuova classe denominata WebFaultException<T> che semplifica la trasmissione di "errori Web" (ad esempio errori HTTP) ai consumer. Funziona molto come FaultException<T> in WCF, ma è possibile specificare un codice di stato HTTP e un tipo di dettaglio per fornire altri dettagli.

Nell'esempio seguente viene illustrato come restituire un errore HTTP 400 (richiesta non valida) quando l'utente specifica un tipo di formato non supportato: si usa semplicemente la stringa per il tipo di dettaglio:

se (!string). IsNullOrEmpty(format))

{

    se (formato. Equals("xml", System.StringComparison.OrdinalIgnoreCase))

    {

        WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

    }

    else if (formato. Equals("json", System.StringComparison.OrdinalIgnoreCase))

    {

        WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

    }

    else

    {

        genera una nuova stringa WebFaultException<(string>). Format("Formato non supportato '{0}'",

           format), HttpStatusCode.BadRequest);

    }

}

Questa nuova funzionalità rende molto più naturale restituire messaggi di errore HTTP standard generando semplicemente eccezioni come normalmente eseguite nei servizi basati su SOAP.

Integrazione di WCF con route ASP.NET

Esistono molte somiglianze tra le funzionalità di routing basate su URL presenti sia in WCF 4 che in ASP.NET 4. Entrambi consentono di eseguire la stessa operazione: eseguire essenzialmente il mapping dei modelli URL ai metodi nelle classi. Esiste tuttavia una differenza significativa tra i due modelli.

L'approccio WCF 4 associa la progettazione del modello URL a una singola classe tramite gli attributi [WebGet] e [WebInvoke] applicati alla definizione della classe durante lo sviluppo. Man mano che il servizio cresce e si evolve nel tempo, questo può portare a una progettazione monolitica di grandi dimensioni che non può essere fattoriata in blocchi più piccoli. L'approccio ASP.NET 4, invece, separa la logica di routing dalla definizione della classe di destinazione, consentendo di eseguire il mapping delle route di servizio tra numerose definizioni di classe quando desiderato.

WCF 4 offre ora la possibilità di integrare il motore di routing ASP.NET con i servizi WCF 4, rendendo possibile trarre vantaggio dal modello di routing ASP.NET oltre ai servizi WCF.

Questa operazione viene eseguita nel metodo Global.asax RegisterRoutes sfruttando la nuova classe ServiceRoute che consente di eseguire il mapping di una route ASP.NET a una classe di servizio WCF:

private void RegisterRoutes()

{

   WebServiceHostFactory factory = nuovo WebServiceHostFactory();

   RouteTable.Route.Add(new ServiceRoute("Bookmarks", factory,

      typeof(BookmarkService)));

   RouteTable.Route.Add(new ServiceRoute("Users", factory,

      typeof(UserService)));

}

Si chiama RouteTable.Route.Add due volte per aggiungere nuove route per due diverse classi di servizio WCF. La prima route esegue il mapping di "/Segnalibri" alla classe BookmarkService mentre la seconda route esegue il mapping di "/Users" alla classe UserService. Si noti come entrambe le route sono configurate per l'uso di WebServiceHostFactory.

Quando si usano gli attributi [WebGet] e [WebInvoke] nelle definizioni delle classi di servizio WCF, verranno usati percorsi relativi e saranno relativi alla route ASP.NET specificata qui.

Modelli di progetto REST

Una delle funzionalità ordinate in Visual Studio 2010 è Gestione estensioni, che è possibile accedere da Strumenti | Gestione estensioni. Extension Manager consente a Microsoft e ad altre terze parti di integrare nuovi modelli di progetto, controlli e strumenti nell'esperienza di Visual Studio 2010 con un semplice clic di un pulsante. Per i team di prodotti Microsoft, questo è ottimo perché rende possibile la spedizione delle cose dopo RTM e rendere ancora le estensioni ufficiali fornite da Microsoft.

Se si visualizza Gestione estensioni e si espande il nodo "Modelli", si troverà un nodo figlio denominato "WCF". Fare clic su "WCF" e si dovrebbero visualizzare quattro nuovi modelli di servizio REST WCF, uno per ogni versione .NET (3.5 vs 4.0) e una per linguaggio (C# vs. VB.NET) come illustrato nella figura 22.

Figura 22: Nuovi modelli di progetto REST WCF in Gestione estensioni

È possibile selezionare questi nuovi modelli di progetto e scaricarli nell'installazione locale di Visual Studio 2010 e usarli come qualsiasi altro tipo di progetto. Il nuovo tipo di progetto REST è disponibile in Visual C# | Web | Applicazione del servizio REST WCF (presupponendo che sia stata installata la versione C#).

Quando si usa per creare un nuovo progetto, si noterà che l'applicazione del servizio REST WCF genera la maggior parte delle nuove funzionalità WCF 4 evidenziate in questa sezione.

Altre informazioni

Oltre a ciò che abbiamo descritto qui, WCF 4 include diverse funzionalità REST avanzate e nuove estensioni API WCF che semplificano le operazioni come la gestione di formati di messaggi personalizzati (non XML o JSON), la restituzione di "visualizzazioni" basate su modelli tramite la nuova funzionalità T4, l'implementazione del supporto GET condizionale e ETag, l'implementazione della concorrenza ottimistica e la generazione di collegamenti in uscita.

Per altre informazioni su tutte queste nuove funzionalità WCF 4, incluse quelle che non abbiamo avuto spazio per coprire qui, passare al blog degli endpoint .NET e trovare la voce introduzione a WCF WebHttp Services in .NET 4. Il team ha fornito una serie completa di blog di 12 parti che illustra ogni nuova funzionalità una voce di blog alla volta, insieme a un sacco di codice di esempio e istruzioni dettagliate.

Servizi flusso di lavoro

Una delle aree di funzionalità che hanno ricevuto la maggior attenzione in .NET 4 è stata quella dei "servizi del flusso di lavoro". Microsoft ha investito molto per migliorare l'integrazione tra WCF e WF per fornire un modello di programmazione avanzato e dichiarativo per la creazione di un'ampia gamma di applicazioni.

Informazioni sui servizi del flusso di lavoro

WF offre un modello di programmazione dichiarativo che genera il livello di astrazione per coloro che scrivono la logica. Ciò che WF offre in definitiva è un framework per scrivere le proprie lingue definendo la propria libreria di attività di dominio aziendale. Offre quindi una finestra di progettazione visiva per la composizione di tali attività in programmi e un runtime che sa come gestire l'esecuzione di tali programmi.

WF offre un modello particolarmente valido per l'implementazione di applicazioni a esecuzione prolungata che devono attendere che si verifichino diversi tipi di eventi esterni, ad esempio la ricezione di un messaggio da un altro sistema. Il modello di persistenza rende possibile l'uso efficiente delle risorse di sistema mantenendo il programma in memoria solo quando è in esecuzione. Quando il programma è in attesa che si verifichi un evento esterno, può essere mantenuto nel database liberando così le risorse di sistema in uso.

Figura 23: Servizi flusso di lavoro

La cosa che rende WF diverso da altri framework di sviluppo è che offre queste funzionalità, consentendo comunque di programmare usando una logica di programmazione di controllo del flusso sequenziale, che in genere è molto più facile per gli sviluppatori leggere e scrivere il codice per comprendere.

Se si creano servizi WCF che sono in esecuzione prolungata o possono trarre vantaggio da alcuni degli altri vantaggi descritti, è possibile implementare i servizi WCF usando un flusso di lavoro WF. È inoltre possibile usare WF per coordinare la logica di utilizzo di altri servizi esterni.

La figura 23 illustra questi concetti.

Anche se questo è stato possibile da .NET 3.5, .NET 4 ha fatto passi significativi per migliorare l'esperienza di sviluppo e fornire alcune delle funzionalità necessarie mancanti in .NET 3.5.

Combinando i mondi di WCF e WF, si ottiene il meglio di entrambi i mondi. Si ottiene un modello di programmazione dichiarativo, un'esperienza descrittiva per gli sviluppatori grazie ai progettisti di WF, un runtime potente per la gestione di servizi a esecuzione prolungata e la flessibilità di comunicazione avanzata offerta da WCF.

Creazione del primo servizio flusso di lavoro

Per offrire un'esperienza di sviluppo dei nuovi servizi di flusso di lavoro, si esaminerà un esempio completo di compilazione usando .NET 4 e la nuova finestra di progettazione di Visual Studio 2010.

Se si apre Visual Studio 2010 e si seleziona File | Nuovo progetto, si troverà un nuovo progetto del flusso di lavoro nei modelli WCF: si chiama "Applicazione del servizio flusso di lavoro WCF". Selezionare questo modello di progetto e assegnargli un nome "HelloWorldWorkflowService" e creare il nuovo progetto.

Figura 24: Modelli di progetto di Servizi flusso di lavoro di Visual Studio 2010

Una volta creato, il nuovo progetto conterrà semplicemente due file, ovvero un file Service1.xamlx contenente la definizione del servizio del flusso di lavoro dichiarativo e un file Web.config contenente la configurazione del servizio.

Uno degli aspetti più interessanti del nuovo modello di servizi flusso di lavoro in .NET 4 è che l'intera definizione del servizio può essere definita in XAML.   Nel nuovo progetto il file Service1.xamlx contiene la definizione del servizio e l'implementazione è semplicemente un flusso di lavoro basato su XAML. Il file Web.config contiene la configurazione del servizio WCF, in cui verranno definiti gli endpoint e i comportamenti del servizio flusso di lavoro.

Nella figura 25 viene illustrato l'aspetto della finestra di progettazione di Visual Studio 2010 per il file Service1.xamlx. Si noti che si tratta solo della finestra di progettazione del flusso di lavoro standard, solo in questo particolare caso il flusso di lavoro che si sta progettando fungerà da implementazione del servizio WCF. La chiave per integrare questa definizione del flusso di lavoro con WCF è il nuovo WorkflowServiceHost e il set di attività di messaggistica WCF (vedere la casella degli strumenti nella figura 25).

Figura 25: Progettazione di un servizio flusso di lavoro in Visual Studio 2010

Lo scheletro del servizio flusso di lavoro fornito con questo modello di progetto contiene un'attività Receive seguita da un'attività Send. Si noti che l'attività Receive contiene una proprietà OperationName ed è attualmente impostata su "GetData". Ha anche una proprietà Content per l'associazione del messaggio in ingresso a una variabile locale definita all'interno dell'attività Sequence. In questo caso, la variabile è denominata "data" ed è di tipo Int32 (vedere la finestra Variabili sotto il servizio di progettazione del flusso di lavoro nella figura 25).

L'attività Receive è configurata anche per attivare il servizio, il che significa che nel messaggio in ingresso può essere creata una nuova istanza del flusso di lavoro. Si noti che la proprietà CanCreateInstance è selezionata all'interno del Finestra Proprietà a destra nella figura 25).

È ora possibile personalizzare il servizio flusso di lavoro. Prima di tutto cambierò il nome file del servizio da Service1.xamlx a HelloWorld.xamlx. Il nome del servizio verrà quindi modificato in "HelloWorldService" nella finestra appropriata. Successivamente cambierò la proprietà OperationName dell'attività Receive in "SayHello". Infine, definirò una nuova variabile denominata "personName" di tipo String all'interno della sequenza.

Associare quindi la proprietà Content dell'attività Receive alla variabile "personName", come illustrato nella figura 26. Successivamente associerò la proprietà Content dell'attività Send a una stringa nel formato "hello {0}!" inserimento della variabile personName nel segnaposto. Salvare quindi il file Service1.xamlx risultante.

Figura 26: Definizione della proprietà Content per l'attività Receive

A questo punto, procedere e aprire il file HelloWorld.xamlx e controllarne il contenuto. A tale scopo, fare clic con il pulsante destro del mouse sul file in Esplora soluzioni e scegliere "Apri con..." seguito da "Editor XML". In questo modo è possibile esaminare la definizione XAML non elaborata per il servizio. È importante notare che la definizione XAML rappresenta l'implementazione completa del servizio. Non esiste codice C#.

Per questo esempio, ci si basa sull'endpoint HTTP predefinito e si presuppone che sia presente un comportamento del servizio standard che abilita automaticamente i metadati del servizio in modo che non sia necessaria alcuna configurazione WCF.

A questo momento è possibile testare il servizio flusso di lavoro basato su XAML. Questa operazione è semplice quanto premere F5 in Visual Studio 2010. Premendo F5 il servizio flusso di lavoro verrà caricato nel server di sviluppo ASP.NET e verrà visualizzato il client di test WCF. Il client di test WCF si connette automaticamente al servizio flusso di lavoro, scarica i metadati WSDL e consente di testare la logica del servizio flusso di lavoro (vedere la figura 27).

Figura 27: Test del servizio flusso di lavoro

Ciò illustra che l'infrastruttura dei servizi flusso di lavoro è in grado di produrre dinamicamente una definizione WSDL per il servizio esaminando le attività di invio e ricezione usate all'interno della definizione del flusso di lavoro e i tipi di messaggi inviati e ricevuti.

Questa procedura completa la semplice procedura dettagliata per la creazione del primo servizio flusso di lavoro dichiarativo in .NET 4. È stato rilevato che l'implementazione del servizio è definita completamente in XAML e che si usano le attività di invio/ricezione per modellare le interazioni di messaggistica all'interno del flusso di lavoro. Si è anche visto che .NET 4 include il supporto dell'hosting per i file con estensione xamlx, eliminando così la necessità di file con estensione svc aggiuntivi. I dettagli di ognuna di queste aree verranno approfonditi in modo più dettagliato nelle sezioni seguenti.

Hosting di servizi flusso di lavoro

.NET 4 include un'infrastruttura di hosting nuova e migliorata per i servizi flusso di lavoro. È possibile ospitare servizi flusso di lavoro in IIS/WAS o nelle proprie applicazioni usando WorkflowServiceHost. L'infrastruttura di hosting .NET 4 fornisce le parti necessarie per la gestione delle istanze del flusso di lavoro a esecuzione prolungata e per la correlazione dei messaggi quando sono presenti più istanze del servizio in esecuzione simultaneamente. .NET 4 fornisce anche un endpoint di controllo del flusso di lavoro standard per la gestione remota delle istanze del flusso di lavoro.

Hosting in IIS/ASP.NET

L'esempio helloWorldWorkflowService semplice illustrato nella sezione precedente illustra come ospitare i servizi del flusso di lavoro in IIS/ASP.NET. Anche se il servizio è stato testato usando ASP.NET Development Server, avrebbe funzionato allo stesso modo all'interno di IIS usando un pool di applicazioni ASP.NET 4. .NET 4 installa il gestore necessario per le richieste con estensione xamlx, che gestisce la creazione di WorkflowServiceHost in background e l'attivazione delle singole istanze del flusso di lavoro.

Anche se nel primo esempio è stato usato l'approccio "zero config", è possibile configurare i servizi del flusso di lavoro all'interno di Web.config come qualsiasi altro servizio WCF. In questo caso è possibile configurare tutti gli endpoint e i comportamenti WCF che si vuole usare. È possibile esporre tutti gli endpoint desiderati nei servizi flusso di lavoro, ma è consigliabile usare una delle nuove associazioni di "contesto", ad esempio BasicHttpContextBinding, WSHttpContextBinding o NetTcpContextBinding, che gestiscono la comunicazione specifica dell'istanza.

L'esempio seguente illustra come configurare un servizio flusso di lavoro con due endpoint HTTP:

<configurazione>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService">

        <endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding"

                  contract="IHelloWorld"/>

      </service>

      ...

È anche possibile configurare il servizio flusso di lavoro con comportamenti WCF. L'esempio seguente illustra come configurare questo servizio flusso di lavoro con alcuni dei comportamenti WCF standard:

<configurazione>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService"

        behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >

        <endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding"

                  contract="IHelloWorld"/>

      </service>

     </Servizi>

    <Comportamenti>

      <comportamentiServizio>

        <behavior name="HelloWorldWorkflowService.Service1Behavior">

          <serviceDebug includeExceptionDetailInFaults="False" />

          <serviceMetadata httpGetEnabled="True"/>

        </Comportamento>

      </Servicebehaviors>

      ...

Oltre a questi comportamenti WCF standard, .NET 4 include alcuni nuovi comportamenti specifici di WF per il controllo della persistenza del flusso di lavoro, il rilevamento del flusso di lavoro e altri comportamenti di runtime del flusso di lavoro in combinazione con i servizi del flusso di lavoro. Per altri dettagli, vedere gli esempi di .NET 4 SDK.

Self-hosting con WorkflowServiceHost

Anche se .NET 3.5 è dotato di una classe WorkflowServiceHost per l'hosting dei servizi del flusso di lavoro, è necessario riprogettare tutte le modifiche apportate al runtime e al modello di programmazione WF in .NET 4. Di conseguenza, .NET 4 include una nuova classe WorkflowServiceHost disponibile nell'assembly System.ServiceModel.Activities.

La classe WorkflowServiceHost semplifica l'hosting dei servizi del flusso di lavoro nell'applicazione, sia che si tratti di un'applicazione console, di un'applicazione WPF o di un servizio Windows. Il frammento di codice seguente illustra come usare WorkflowServiceHost all'interno del codice dell'applicazione:

...

Host WorkflowServiceHost = nuovo WorkflowServiceHost("HelloWorld.xamlx",

    new Uri("https://localhost:8080/helloworld"));

Host. AddDefaultEndpoints();

Host. Description.Behaviors.Add(

    new ServiceMetadataBehavior { HttpGetEnabled = true });

Host. Open();

Console.WriteLine("Host è aperto");

Console.ReadLine();

...

Come si può notare, se si sceglie di ospitare i servizi del flusso di lavoro in IIS/ASP.NET o nelle proprie applicazioni, sono altrettanto facili da ospitare come qualsiasi servizio WCF.

WorkflowControlEndpoint

L'hosting di un servizio flusso di lavoro si occupa dell'inizializzazione del runtime di WF e dell'attivazione di nuove istanze del flusso di lavoro durante l'attivazione dei messaggi arrivano tramite uno degli endpoint esposti. Oltre a questa funzionalità di hosting di base, è anche importante essere in grado di gestire la configurazione e l'esecuzione di queste istanze del flusso di lavoro in esecuzione in remoto. .NET 4 semplifica questa operazione fornendo un oggetto WorkflowControlEndpoint standard che è possibile esporre nei servizi del flusso di lavoro.

L'esempio seguente illustra come aggiungere l'endpoint di controllo del flusso di lavoro standard al servizio in uno scenario di hosting personalizzato:

...

Host WorkflowServiceHost = nuovo WorkflowServiceHost("HelloWorld.xamlx",

    new Uri("https://localhost:8080/helloworld"));

Host. AddDefaultEndpoints();

WorkflowControlEndpoint wce = new WorkflowControlEndpoint(

    new NetNamedPipeBinding(),

    new EndpointAddress("net.pipe://localhost/helloworld/WCE"));

Host. AddServiceEndpoint(wce);

Host. Open();

...

Se si ospita in IIS/ASP.NET, è possibile aggiungere lo standard WorkflowControlEndpoint come indicato di seguito:

<configurazione>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService"

          behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >

        <endpoint address="" binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="wce" binding="wsHttpBinding" kind="workflowControlEndpoint"/>

      </service>

      ...

WorkflowControlEndpoint consente di gestire il servizio flusso di lavoro all'interno di ambienti di hosting diversi. Un ambiente di questo tipo è reso possibile da Windows Server AppFabric, che sarà disponibile in una versione futura di Windows Server.

Attività di invio/ricezione

.NET 3.5 è stato fornito con due attività di messaggistica, Send and Receive, per l'invio e la ricezione di messaggi tramite WCF, ma sono stati abbastanza limitati in termini di funzionalità. .NET 4 migliora le attività di invio e ricezione con alcune funzionalità aggiuntive (ad esempio, correlazione) e aggiunge altre attività di messaggistica, SendReply e ReceiveReply, che semplificano la modellazione delle operazioni di richiesta-risposta.

Quando si usano le attività di invio/ricezione all'interno di un servizio flusso di lavoro, si usano essenzialmente per modellare il contratto di servizio che verrà esposto ai client tramite la definizione WSDL. Quando si usa l'attività Receive, assegnargli un nome di operazione ed eseguire il mapping del messaggio in arrivo a un tipo .NET. Anche se finora sono state usate stringhe semplici, è anche possibile usare contratti dati definiti dall'utente complessi. Aggiornare HelloWorldWorkflowService per usare il tipo di contratto dati seguente:

[DataContract(Namespace="")]

public class Person

{

    [DataMember]

    public string ID;

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

Se aggiungiamo questa definizione di classe al progetto Web e torniamo a HelloWorld.xamlx, possiamo definire una nuova variabile denominata "personMsg" di tipo Person (il contratto dati definito in precedenza). È quindi possibile eseguire il mapping delle attività ReceiveRequest e SendResponse alla variabile personMsg tramite la proprietà "Content". A tale scopo, è sufficiente selezionare ogni attività e premere il pulsante "Contenuto" per visualizzare la finestra "Definizione contenuto". Immettere quindi "personMsg" nella casella di testo "Message Data" e "Person" nella casella di testo "Message Type". È necessario eseguire questa operazione per entrambe le attività.

Le attività di invio/ricezione consentono inoltre di configurare altri aspetti del servizio WCF, ad esempio il nome dell'operazione, il nome del contratto di servizio, l'azione, la raccolta di tipi noti, il livello di protezione e il serializzatore da usare in fase di esecuzione (ad esempio, DataContractSerializer e XmlSerializer). Nell'attività Invia si specificano anche i dettagli dell'endpoint di destinazione. Tutte queste impostazioni sono configurabili tramite il Finestra Proprietà quando è selezionata l'attività Invia/Ricezione.

Con queste modifiche sul posto, puoi testare di nuovo HelloWorld.xamlx per vedere che l'operazione SayHello è ora definita per ricevere e restituire messaggi di tipo Person.

Definizione delle operazioni di Request-Reply

Per semplificare il modello delle operazioni request-reply, .NET 4 introduce alcune nuove attività per la modellazione di questi tipi di interazioni. Una è l'attività SendReply che è possibile combinare con un'attività Receive per implementare un'operazione request-reply all'interno del servizio. È anche disponibile un'attività ReceiveReply che è possibile combinare con un'attività Send quando si richiama un servizio esterno all'interno di un flusso di lavoro.

.NET 4 include anche alcune attività di livello superiore denominate ReceiveAndSendReply e SendAndReceiveReply, che verranno visualizzate nella casella degli strumenti di Visual Studio 2010. Queste attività compongono semplicemente Receive/Send con SendReply/ReceiveReply rispettivamente e semplificano l'uso. Quando si trascinano nell'area di progettazione del flusso di lavoro, si noterà che si espande in una sequenza che contiene un'attività Di invio o ricezione seguita dall'attività "reply" appropriata.

Aggiungi riferimento al servizio

Per semplificare ulteriormente l'utilizzo di servizi esterni, Visual Studio 2010 offre anche una funzionalità "Aggiungi riferimento al servizio" che funziona come previsto, ad eccezione della generazione di una definizione di classe proxy client che genera un set di attività lato client. Dopo aver selezionato "Aggiungi riferimento al servizio" e aver specificato l'indirizzo della definizione WSDL, Visual Studio scarica il file WSDL e genera un'attività personalizzata per ogni operazione trovata nella definizione WSDL.

Di seguito viene illustrato un semplice esempio. Si supponga che sia presente un CalculatorService con operazioni Add, Subtract, Multiply e Divide che si vuole usare dal servizio flusso di lavoro. È possibile selezionare semplicemente "Aggiungi riferimento al servizio" e specificare la posizione della definizione WSDL CalculatorService, come illustrato nella figura 28.

Figura 28: Aggiungere riferimenti al servizio

Quando si preme OK per aggiungere il riferimento al servizio, Visual Studio scarica la definizione WSDL e genera quattro attività personalizzate che verranno visualizzate nella casella degli strumenti. Sono ora disponibili attività Add, Subtract, Multiply e Divide che sono facili da usare all'interno dei flussi di lavoro per richiamare il servizio esterno.

Correlation

Quando si creano sistemi costituiti da servizi flusso di lavoro a esecuzione prolungata, è comune avere numerose istanze di un singolo servizio flusso di lavoro in esecuzione simultaneamente in attesa dello stesso evento (ad esempio, in attesa di un messaggio specifico per l'arrivo prima di continuare). La sfida con questo scenario è che è necessario un modo per correlare il messaggio in ingresso con l'istanza del flusso di lavoro corretta. In genere, il modo in cui si determina l'istanza del flusso di lavoro corretta consiste nell'esaminare il contenuto del messaggio in arrivo.

.NET 4 include una sofisticata funzionalità di correlazione dei messaggi basata sul contenuto che è possibile usare in combinazione con le attività di invio e ricezione appena descritte. L'implementazione di questa funzionalità si basa su ciò che è noto come "handle di correlazione", un altro nuovo concetto in .NET 4.

Per iniziare a usare la correlazione, è necessario innanzitutto definire una variabile del flusso di lavoro di tipo CorrelationHandle. È possibile considerare questa variabile come il punto di incontro per la connessione di una parte di dati da (potenzialmente) due messaggi diversi (elaborati da due diverse attività di messaggistica). Entrambe le attività Send e Receive forniscono una proprietà CorrelatesWith per specificare una variabile CorrelationHandle.

Per correlare i messaggi inviati da più attività di invio/ricezione, è necessario specificare lo stesso handle di correlazione in tutte le attività che desiderano partecipare alla correlazione. In ogni attività viene quindi configurata la chiave di correlazione a cui verrà eseguito il mapping sul messaggio elaborato da tale attività specifica( ad esempio, l'elemento SSN in un messaggio potrebbe essere correlato all'elemento CustomerId in un altro messaggio). Le chiavi di correlazione vengono definite usando espressioni XPath valutate rispetto ai messaggi.

Estendere il servizio HelloWorldWorkflowService per vedere un esempio di funzionamento. Attualmente, l'esempio con cui si sta lavorando riceve un messaggio Person e lo restituisce al client. Aggiungere un'altra attività ReceiveAndSendReply subito sotto l'attività SendResponse nella parte inferiore del flusso di lavoro. In questo modo viene aggiunta la sequenza che contiene un altro oggetto Receive seguito da un altro oggetto SendReply. Darò all'attività Receive un nome dell'operazione "Finish" e il client dovrà fornire un messaggio di saluto nel messaggio Finish che verrà usato per costruire la risposta di saluto finale.

In altre parole, i client chiameranno prima SayHello specificando il nome completo per avviare una nuova istanza del servizio flusso di lavoro. L'istanza del flusso di lavoro attenderà quindi fino a quando il client chiama Finish fornendo una formula di saluto (e ciò potrebbe richiedere potenzialmente minuti, ore o giorni, durante il quale il flusso di lavoro potrebbe persistere). Dopo che il client chiama Finish fornendo la formula di saluto, il flusso di lavoro compila un messaggio di saluto usando il saluto e il nome originale e lo restituisce. In questo scenario, è possibile avere facilmente più istanze del flusso di lavoro in esecuzione in attesa della chiamata dell'operazione Fine, quindi sarà sicuramente necessario correlare il messaggio Finish con il messaggio SayHello precedente. Poiché questo è il caso, è necessario associare l'attività Receive per "Finish" allo stesso handle di correlazione specificato nell'attività "SayHello" (nameHandle).

Si esaminerà ora il contenuto del messaggio che verrà correlato tra queste due attività. Per questo esempio si useranno i due tipi di contratto dati seguenti per modellare i messaggi:

[DataContract(Namespace="")]

public class Person

{

    [DataMember]

    public string FirstName { get; set; }

    [DataMember]

    public string LastName { get; set; }

}

[DataContract(Namespace = "")]

public class Greeting

{

    [DataMember]

    public string Salutation { get; set; }

    [DataMember]

    public string NameKey { get; set; }

}

L'attività Receive per "SayHello" è configurata per l'utilizzo del messaggio Person e l'attività Receive per "Finish" è configurata per l'utilizzo del messaggio Greeting. Si presuppone che il valore LastName sia sempre univoco in modo da poterlo usare come valore univoco per la correlazione. Di conseguenza, quando il client invia il messaggio "Finish", deve fornire lo stesso valore LastName usato nel messaggio "SayHello" precedente.

Si vedrà ora come configurare l'handle di correlazione per configurare questa impostazione. È già stata definita una variabile CorrelationHandle all'interno della sequenza denominata "handle". La prima cosa da fare è "inizializzare" l'handle di correlazione all'interno dell'attività di ricezione "SayHello". Selezionare quindi l'attività di ricezione "SayHello" e premere il pulsante accanto alla casella di testo "CorrelationInitializers".

Qui è necessario selezionare "Inizializzatore di correlazione query" nella casella di riepilogo a discesa e quindi si dovrebbe essere in grado di scegliere la proprietà "LastName" per il campo di query(dovrebbe produrre un'espressione XPath di "sm:body()/xg0:Person/xg0:LastName" come illustrato nella figura 29.

È quindi necessario specificare che l'attività di ricezione "Fine" con correlazione sullo stesso handle. Selezionare l'attività di ricezione "Fine" e premere il pulsante "CorrelatesOn". Specificare quindi "handle" per l'handle "CorrelatesWith" e quindi selezionare "NameKey" per il campo della query (vedere la figura 30).

Figura 29: Definizione di una chiave di correlazione per "SayHello"

Figura 30: Definizione di una chiave di correlazione per "Fine"

Ciò significa che l'elemento LastName nel messaggio Person deve corrispondere all'elemento NameKey nel messaggio Greeting tra le due richieste separate. Con questa impostazione, l'infrastruttura del flusso di lavoro sarà in grado di correlare automaticamente i messaggi per microsoft e instradare i messaggi "Finish" in arrivo all'istanza corretta del flusso di lavoro (in base a "LastName/NameKey").

L'attività SendReply finale restituisce la stringa formattata seguente al chiamante, incluse le informazioni del "personMsg" originale e il nuovo "greetingMsg":

String.Format("{0}{1}{2}!",

   greetingMsg.Salutation, personMsg.FirstName, personMsg.LastName)

Si testerà la funzionalità di correlazione usando il client di test WCF per avviare diverse istanze del flusso di lavoro chiamando SayHello più volte con valori FirstName e LastName diversi. Dopo aver eseguito questa operazione, dovrebbero essere presenti diverse istanze in esecuzione del servizio flusso di lavoro. A questo punto, è possibile chiamare l'operazione Finish specificando uno degli stessi valori LastName usati in uno dei messaggi SayHello e verrà visualizzato il valore FirstName corrispondente usato nel messaggio di saluto finale restituito al client (vedere la figura 31).

Questo illustra la correlazione basata sul contenuto in azione, una funzionalità interessante dei servizi flusso di lavoro fornita con .NET 4. È importante notare che è anche possibile eseguire la correlazione in base ai dati a livello di canale e protocollo, come in .NET 3.5, ad esempio usando il canale o l'ID istanza del flusso di lavoro. La correlazione basata sul contenuto è un approccio più flessibile e sofisticato per eseguire la stessa operazione.

Figura 31: Test dell'esempio di correlazione basata sul contenuto

Questo ci porta alla fine della copertura dei servizi del flusso di lavoro. In questo documento è stato possibile graffiare solo la superficie dei servizi del flusso di lavoro, ma sarà possibile trovare informazioni aggiuntive negli esempi di .NET 4 SDK e nella documentazione MSDN in continua crescita disponibile online. Come si può notare, la combinazione di WCF e WF 4 apre le porte a un modello di sviluppo completamente nuovo per la creazione di servizi dichiarativi, a esecuzione prolungata e asincroni che possono usufruire delle migliori funzionalità che entrambi i framework devono offrire.

Varie funzionalità avanzate

Oltre a tutto il resto di cui abbiamo parlato in questo documento, WCF 4 include alcune funzionalità più avanzate che possono risultare utili. Queste funzionalità avanzate includono il supporto migliorato per la risoluzione dei tipi tramite DataContractResolver, la possibilità di gestire consumer di code concorrenti tramite ReceiveContext, un nuovo codificatore di flusso di byte e la traccia basata su ETW ad alte prestazioni.

Risoluzione dei tipi con DataContractResolver

In WCF 3.x esiste una funzionalità di risoluzione dei tipi denominata "tipi noti". Durante la deserializzazione, quando il serializzatore rileva un'istanza che non è dello stesso tipo del tipo dichiarato, controlla l'elenco di "tipi noti" dichiarati per determinare il tipo da usare. Come autore del servizio, è possibile annotare i tipi/metodi con gli attributi [KnownType] o [ServiceKnownType] per definire l'elenco delle possibili sostituzioni. Questa funzionalità viene comunemente usata per supportare l'ereditarietà e il polimorfismo.

Purtroppo WCF 3.x non offre un modo semplice per eseguire l'override dell'algoritmo di mapping dei tipi usato da DataContractSerializer durante l'esecuzione di questo tipo di risoluzione dinamica dei tipi in fase di esecuzione. Per risolvere il problema, WCF 4 fornisce la classe abstract DataContractResolver da cui è possibile derivare per implementare un algoritmo di risoluzione dei tipi personalizzato.  Di seguito viene illustrato come iniziare:

classe MyDataContractResolver : DataContractResolver

{

    Assembly assembly;

    public MyDataContractResolver(Assembly)

    {

        this.assembly = assembly;

    }

    Usato in fase di deserializzazione

    Consente agli utenti di eseguire il mapping di xsi:type name a qualsiasi tipo

    public override Type ResolveName(string typeName, string typeNamespace,

        Type declaredType, DataContractResolver knownTypeResolver)

    {

        ... // implementare il mapping

    }

    Usato in fase di serializzazione

    Esegue il mapping di qualsiasi tipo a una nuova rappresentazione xsi:type

    public override bool TryResolveType(Type, Type declaredType,

        DataContractResolver knownTypeResolver, out XmlDictionaryString typeName,

        out XmlDictionaryString typeNamespace)

    {

        ... // implementare il mapping

    }

}

Dopo aver implementato DataContractResolver personalizzato, è possibile fornirlo a DataContractSerializer, come illustrato di seguito:

DataContractSerializer dcs = new DataContractSerializer(

    typeof(Object), null, int. MaxValue, false, true, null,

    new MyDataContractResolver(assembly));

Quando si usa questa istanza di DataContractSerializer per serializzare/deserializzare gli oggetti, verrà chiamato datacontractResolver personalizzato per eseguire la risoluzione dei tipi personalizzata.

Per inserire il datacontractResolver personalizzato nel runtime WCF in background, è necessario scrivere un comportamento del contratto WCF che si collega a DataContractSerializerOperationBehavior ed esegue l'override del resolver predefinito. .NET 4 SDK include un esempio completo che illustra come eseguire questa operazione tramite un comportamento del contratto e un attributo personalizzato denominato [KnownAssembly].

La risoluzione dei tipi personalizzati può essere utile quando si desidera eseguire l'override delle impostazioni predefinite di DataContractSerializer, controllare esattamente quali tipi vengono usati per la serializzazione o per gestire in modo dinamico i tipi noti.

Elaborazione di messaggi in coda con ReceiveContext

Con WCF 3.x, è possibile garantire il recapito di messaggi esattamente una sola volta quando si usa NetMsmqBinding usando le code transazionali e l'integrazione dell'operazione del servizio WCF nella transazione MSMQ. Quando vengono generate eccezioni durante l'elaborazione di un messaggio, WCF garantisce che il messaggio non venga perso restituendo il messaggio a una coda(potrebbe essere restituito alla coda di origine, a una coda di messaggi non elaborabili o a una coda di messaggi non recapitabili a seconda della configurazione).

Anche se questa funzionalità è importante, ci sono alcuni problemi che le persone comunemente riscontrano. Una è che le transazioni sono costose e produce un sacco di lettura/scrittura con le code, che introduce un sovraccarico e una complessità maggiori. Un altro problema è che non esiste alcun modo per garantire che lo stesso servizio elabori il messaggio alla successiva estrazione dalla coda (ad esempio, non esiste alcun modo per un particolare servizio per "bloccare" un messaggio), che è una garanzia che può essere utile in determinati scenari.

Per risolvere questi problemi, WCF 4 introduce una nuova API denominata ReceiveContext per l'elaborazione dei messaggi in coda. Con ReceiveContext, un servizio può "visualizzare" un messaggio nella coda per iniziare a elaborarlo e, se qualcosa va storto e viene generata un'eccezione, rimane nella coda. I servizi possono anche bloccare i messaggi per ritentare l'elaborazione in un momento successivo. ReceiveContext fornisce un meccanismo per "completare" il messaggio una volta elaborato in modo che possa essere rimosso dalla coda.

Questo approccio semplifica le operazioni in diversi fronti perché i messaggi non vengono più letti e riscritto nelle code in rete e i singoli messaggi non rimbalzano tra istanze del servizio diverse durante l'elaborazione. Di seguito viene illustrato un semplice esempio per illustrare il funzionamento.

L'esempio seguente illustra come usare ReceiveContext per elaborare i messaggi in arrivo in una coda:

...

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

[ReceiveContextEnabled(ManualControl = true)]

public void CalculateProduct(int firstNumber, int secondNumber)

{

    ReceiveContext receiveContext;

    se (! ReceiveContext.TryGet(OperationContext.Current.IncomingMessageProperties,

         out receiveContext))

    {

        Console.WriteLine("ReceiveContext non installato/trovato nel computer.");

        return;

    }

    if ((firstNumber * secondNumber) % 2 == (receiveCount % 2))

    {

        receiveContext.Complete(TimeSpan.MaxValue);

        Console.WriteLine("{0} x {1} = {2}", firstNumber, secondNumber,

            firstNumber * secondNumber);

    }

    else

    {

        receiveContext.Abandon(TimeSpan.MaxValue);

        Console.WriteLine("{0}&{1} not processed", firstNumber, secondNumber);

    }

    receiveCount++;

}

...

Supponendo un passaggio riuscito, viene chiamato ReceiveContext.Complete per completare l'elaborazione del messaggio che ne causa la rimozione dalla coda sottostante. In caso contrario, chiamiamo Abandon, che lascia il messaggio nella coda per i tentativi futuri. .NET 4 SDK include un esempio completo che è possibile usare per esplorare questa nuova funzionalità in modo più approfondito.

Semplificare la codifica con ByteStreamMessageEncodingBindingElement

In alcuni scenari di messaggistica è sufficiente trasmettere "BLOB" di dati binari senza eseguire il wrapping o l'elaborazione aggiuntiva. Per semplificare questo scenario, WCF 4 viene fornito con un nuovo ByteStreamMessageEncodingBindingElement che esegue questa operazione. Sfortunatamente, .NET 4 non include una nuova associazione per questo codificatore, quindi è necessario creare un'associazione personalizzata per usarla.

.NET 4 SDK include un esempio completo che illustra come definire un'associazione personalizzata che usa ByteStreamMessageEncodingBindingElement tramite una classe di associazione personalizzata. Dopo aver creato la nuova classe di associazione, il codificatore del flusso di byte diventa molto semplice da usare con i servizi.

Traccia basata su ETW ad alte prestazioni

WCF 4 apporta anche alcuni miglioramenti sul fronte della traccia e della diagnostica. WCF 4 ora usa ETW per la traccia, che migliora notevolmente le prestazioni di traccia e offre una migliore integrazione con altre tecnologie correlate come Windows Workflow Foundation, Windows Server AppFabric e le varie tecnologie di gestione trovate in Windows Server, offrendo un modello migliore per la piattaforma.

Con ETW, i provider di eventi scrivono eventi in sessioni e sessioni possono facoltativamente rendere persistenti tali eventi in un log. I consumer possono ascoltare gli eventi di sessione in tempo reale o leggere tali eventi da un log dopo il fatto. I controller ETW sono responsabili dell'abilitazione/disabilitazione delle sessioni e dell'associazione dei provider alle sessioni.

.NET 4 SDK offre un semplice esempio che illustra come sfruttare questa nuova architettura di traccia ad alte prestazioni in combinazione con i servizi WCF.

Conclusione

WCF 4 offre numerosi miglioramenti e diverse funzionalità completamente nuove che affrontano alcuni degli scenari di comunicazione più comuni di oggi. Innanzitutto, WCF 4 diventa più semplice da usare tramite il modello di configurazione semplificato e il supporto migliore per le impostazioni predefinite comuni.

WCF 4 offre inoltre il supporto di prima classe per l'individuazione e il routing dei servizi, che sono requisiti comuni nella maggior parte degli ambienti aziendali e di grandi iniziative SOA: queste funzionalità usano solo WCF a parte molti dei framework concorrenti. WCF 4 semplifica anche lo sviluppo di servizi basati su REST e offre diverse altre funzionalità WCF più avanzate che consentono di risolvere alcuni punti di dolore specifici oggi.

Oltre a tutto questo, WCF 4 offre un'integrazione sofisticata con WF per fornire un nuovo modello per lo sviluppo di servizi di flusso di lavoro dichiarativi. I servizi del flusso di lavoro consentono di sviluppare servizi a esecuzione prolungata e asincrona che traggono vantaggio dal modello di programmazione WF e dal runtime sottostante. Grazie alle nuove attività basate su WCF e al supporto della finestra di progettazione disponibili all'interno di Visual Studio 2010, questo nuovo modello di programmazione sta diventando un'opzione di prima classe per la creazione di servizi.

In .NET 4 i mondi di WCF e WF si unisce insieme per offrire un modello di programmazione cohesive che offre i migliori entrambi i mondi da offrire. Per altre informazioni sulle novità di WF 4, vedere Introduzione di Uno sviluppatore a Windows Workflow Foundation in .NET 4.

Informazioni sull'autore

Aaron Skonnard è un cofondatore di Pluralsight, un provider di formazione Microsoft che offre corsi di sviluppo .NET guidati da insegnanti e su richiesta. Questi giorni Aaron passa la maggior parte del suo tempo a registrare Pluralsight On Demand! corsi incentrati su Cloud Computing, Windows Azure, WCF e REST. Aaron ha trascorso anni scrivendo, parlando e insegnando sviluppatori professionali in tutto il mondo. È possibile raggiungerlo a http://pluralsight.com/aaron e http://twitter.com/skonnard.

Risorse aggiuntive