Procedura: controllo delle versioni dei serviziHow To: Service Versioning

In questo argomento vengono descritti i passaggi di base necessari per creare una configurazione del routing che indirizza messaggi a versioni diverse dello stesso servizio.This topic outlines the basic steps required to create a routing configuration that routes messages to different versions of the same service. In questo esempio i messaggi vengono indirizzati a due versioni diverse di un servizio di calcolo, roundingCalc (v1) e regularCalc (v2).In this example, messages are routed to two different versions of a calculator service, roundingCalc (v1) and regularCalc (v2). Entrambe le implementazioni supportano le stesse operazioni; tuttavia il primo servizio, roundingCalc, arrotonda tutti i calcoli al valore intero più vicino prima della restituzione.Both implementations support the same operations; however the older service, roundingCalc, rounds all calculations to the nearest integer value before returning. Un'applicazione client deve essere in grado di indicare se utilizzare il secondo servizio, regularCalc.A client application must be able to indicate whether to use the newer regularCalc service.

Avviso

Per indirizzare un messaggio a una specifica versione del servizio, il servizio di routing deve essere in grado di determinare la destinazione del messaggio in base al relativo contenuto.In order to route a message to a specific service version, the Routing Service must be able to determine the message destination based on the message content. Nel metodo illustrato di seguito il client specifica la versione inserendo informazioni in un'intestazione di messaggio.In the method demonstrated below, the client will specify the version by inserting information into a message header. Esistono metodi di controllo delle versioni del servizio che non richiedono il passaggio di dati aggiuntivi da parte dei client.There are methods of service versioning that do not require clients to pass additional data. Un messaggio potrebbe essere ad esempio indirizzato alla versione più recente o più compatibile di un servizio oppure una parte del relativo elemento SOAP Envelope standard potrebbe essere utilizzato dal router.For example, a message could be routed to the most recent or most compatible version of a service or the router could use a part of the standard SOAP envelope.

Le operazioni esposte da entrambi servizi sono:The operations exposed by both services are:

  • AddAdd

  • SubtractSubtract

  • MultiplyMultiply

  • DividiDivide

Poiché entrambe le implementazioni del servizio gestiscono le stesse operazioni e sono essenzialmente identiche tranne che per i dati che restituiscono, i dati di base contenuti nei messaggi inviati dalle applicazioni client non sono sufficientemente univoci per consentire di determinare la modalità di routing della richiesta.Because both service implementations handle the same operations, and are essentially identical other than the data that they return, the base data contained in messages sent from client applications is not unique enough to allow you to determine how to route the request. Non è ad esempio possibile utilizzare i filtri Action, poiché le azioni predefinite per entrambi i servizi sono identiche.For example, Action filters cannot be used because the default actions for both services are the same.

È possibile risolvere questo problema in diversi modi, ad esempio esponendo un endpoint specifico sul router per ogni versione del servizio o aggiungendo un elemento di intestazione personalizzato al messaggio per indicare la versione del servizio.This can be resolved in several ways, such as exposing a specific endpoint on the router for each version of the service or adding a custom header element to the message to indicate service version. Ognuno di questi approcci consente di indirizzare in modo univoco messaggi in ingresso a una versione specifica del servizio, ma l'utilizzo di contenuti univoci nei messaggi è il metodo preferibile per distinguere le richieste indirizzate a versioni diverse del servizio.Each of these approaches allows you to uniquely route incoming messages to a specific version of the service, but utilizing unique message content is the preferred method of differentiating between requests for different service versions.

In questo esempio l'applicazione client aggiunge l'intestazione personalizzata 'CalcVer' al messaggio di richiesta.In this example, the client application adds the ‘CalcVer’ custom header to the request message. Questa intestazione contiene un valore che indica la versione del servizio a cui deve essere indirizzato il messaggio.This header will contain a value that indicates the version of the service that the message should be routed to. Il valore '1' indica che il messaggio deve essere elaborato dal servizio roundingCalc, mentre il valore '2' indica il servizio regularCalc.A value of ‘1’ indicates that the message must be processed by the roundingCalc service, while a value of ‘2’ indicates the regularCalc service. In questo modo l'applicazione client può controllare direttamente quale versione del servizio elaborerà il messaggio.This allows the client application to directly control which version of the service will process the message. Poiché l'intestazione personalizzata è un valore contenuto all'interno del messaggio, è possibile utilizzare un endpoint per ricevere messaggi destinati a entrambe le versioni del servizio.Since the custom header is a value contained within the message, you can use one endpoint to receive messages destined for both versions of the service. Il codice seguente può essere utilizzato nell'applicazione client per aggiungere l'intestazione personalizzata al messaggio:The following code can be used in the client application to add this custom header to the message:

messageHeadersElement.Add(MessageHeader.CreateHeader("CalcVer", "http://my.custom.namespace/", "2"));  

Implementare il controllo delle versioni del servizioImplement Service Versioning

  1. Creare la configurazione del servizio di routing di base specificando l'endpoint servizio esposto dal servizio.Create the basic Routing Service configuration by specifying the service endpoint exposed by the service. Nell'esempio seguente viene definito un solo endpoint servizio che verrà utilizzato per ricevere messaggi.The following example defines a single service endpoint, which will be used to receive messages. Vengono inoltre definiti gli endpoint client che verranno utilizzati per inviare messaggi ai servizi roundingCalc (v1) regularCalc (v2).It also defines the client endpoints which will be used to send messages to the roundingCalc (v1) and the regularCalc (v2) services.

    <services>  
        <service behaviorConfiguration="routingConfiguration"  
                 name="System.ServiceModel.Routing.RoutingService">  
          <host>  
            <baseAddresses>  
              <add baseAddress="http://localhost/routingservice/router" />  
            </baseAddresses>  
          </host>  
          <!--Set up the inbound endpoint for the Routing Service-->  
          <endpoint address="calculator"  
                    binding="wsHttpBinding"  
                    name="routerEndpoint"  
                    contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
        </service>  
    </services>  
    <client>  
    <!--set up the destination endpoints-->  
          <endpoint name="regularCalcEndpoint"  
                    address="net.tcp://localhost:9090/servicemodelsamples/service/"  
                    binding="netTcpBinding"  
                    contract="*" />  
    
          <endpoint name="roundingCalcEndpoint"  
                    address="http://localhost:8080/servicemodelsamples/service/"  
                    binding="wsHttpBinding"  
                    contract="*" />  
        </client>  
    
  2. Definire i filtri usati per indirizzare messaggi agli endpoint di destinazione.Define the filters used to route messages to the destination endpoints. In questo esempio viene utilizzato il filtro XPath per rilevare il valore dell'intestazione personalizzata "CalcVer" per determinare la versione deve essere indirizzato il messaggio a.For this example, the XPath filter is used to detect the value of the "CalcVer" custom header to determine which version the message should be routed to. Un filtro XPath viene utilizzato anche per rilevare i messaggi che non contengono l'intestazione "calcver".An XPath filter is also used to detect messages that do not contain the "CalcVer" header. Nell'esempio seguente vengono definiti la tabella dello spazio dei nomi e dei filtri necessari.The following example defines the required filters and namespace table.

    <!-- use the namespace table element to define a prefix for our custom namespace-->  
    <namespaceTable>  
      <add prefix="custom" namespace="http://my.custom.namespace/"/>  
    </namespaceTable>  
    <filters>  
      <!--define the different message filters-->  
      <!--define an xpath message filter to look for the  
          custom header containing a value of 2-->  
      <filter name="XPathFilterRegular" filterType="XPath"  
              filterData="sm:header()/custom:CalcVer = '2'"/>  
      <!--define an xpath message filter to look for the  
          custom header containing a value of 1-->  
      <filter name="XPathFilterRounding" filterType="XPath"  
              filterData="sm:header()/custom:CalcVer = '1'"/>  
       <!--define an xpath message filter to look for  
           messages that do not contain the custom header-->  
       <filter name="XPathFilterNoHeader" filterType="XPath"  
               filterData="count(sm:header()/custom:CalcVer)=0"/>  
    </filters  
    

    Nota

    Il prefisso S12 dello è definito per impostazione predefinita nella tabella dello spazio dei nomi e rappresenta lo spazio dei nomi "http://www.w3.org/2003/05/soap-envelope".The s12 namespace prefix is defined by default in the namespace table, and represents the namespace "http://www.w3.org/2003/05/soap-envelope".

  3. Definire la tabella dei filtri, che associa ogni filtro a un endpoint client.Define the filter table, which associates each filter with a client endpoint. Se il messaggio contiene l'intestazione "calcver" con un valore pari a 1, verrà inviato al servizio regularCalc.If the message contains the "CalcVer" header with a value of 1, it will be sent to the regularCalc service. Se l'intestazione contiene il valore 2, verrà inviato al servizio roundingCalc.If the header contains a value of 2, it will be sent to the roundingCalc service. Se non è presente alcuna intestazione, il messaggio verrà indirizzato a regularCalc.If no header is present, the message will be routed to the regularCalc.

    Di seguito viene definita la tabella dei filtri e vengono aggiunti i filtri definiti in precedenza.The following defines the filter table and adds the filters defined earlier.

    <filterTables>  
      <filterTable name="filterTable1">  
          <!--add the filters to the message filter table-->  
          <!--look for the custom header = 1, and if we find it,  
              send the message to the rounding calc endpoint-->  
          <add filterName="XPathFilterRounding" endpointName="roundingCalcEndpoint"/>  
          <!--look for the custom header = 2, and if we find it,  
              send the message to the rounding calc endpoint-->  
          <add filterName="XPathFilterRegular" endpointName="regularCalcEndpoint"/>  
          <!--look for the absence of the custom header, and if  
              it is not present, assume the v1 endpoint-->  
          <add filterName="XPathFilterNoHeader" endpointName="roundingCalcEndpoint"/>  
      </filterTable>  
    </filterTables>  
    
  4. Per valutare i messaggi in ingresso rispetto ai filtri contenuti nella rispettiva tabella, è necessario associare la tabella dei filtri agli endpoint servizio tramite il comportamento di routing.To evaluate incoming messages against the filters contained in the filter table, you must associate the filter table with the service endpoints by using the routing behavior. Nell'esempio seguente viene illustrata l'associazione di "filterTable1" agli endpoint servizio:The following example demonstrates associating "filterTable1" with the service endpoints:

    <behaviors>  
      <!--default routing service behavior definition-->  
      <serviceBehaviors>  
        <behavior name="routingConfiguration">  
          <routing filterTableName="filterTable1" />  
        </behavior>  
      </serviceBehaviors>  
    </behaviors>  
    

EsempioExample

Il codice seguente costituisce un elenco completo del file di configurazione.The following is a complete listing of the configuration file.

<?xml version="1.0" encoding="utf-8" ?>  
<!-- Copyright (c) Microsoft Corporation. All rights reserved -->  
<configuration>  
  <system.serviceModel>  
    <services>  
      <service behaviorConfiguration="routingConfiguration"  
               name="System.ServiceModel.Routing.RoutingService">  
        <host>  
          <baseAddresses>  
            <add baseAddress="http://localhost/routingservice/router" />  
          </baseAddresses>  
        </host>  
        <!--Set up the inbound endpoint for the Routing Service-->  
        <endpoint address="calculator"  
                  binding="wsHttpBinding"  
                  name="routerEndpoint"  
                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
      </service>  
    </services>  
    <behaviors>  
      <!--default routing service behavior definition-->  
      <serviceBehaviors>  
        <behavior name="routingConfiguration">  
          <routing filterTableName="filterTable1" />  
        </behavior>  
      </serviceBehaviors>  
    </behaviors>  

    <client>  
<!--set up the destination endpoints-->  
      <endpoint name="regularCalcEndpoint"  
                address="net.tcp://localhost:9090/servicemodelsamples/service/"  
                binding="netTcpBinding"  
                contract="*" />  

      <endpoint name="roundingCalcEndpoint"  
                address="http://localhost:8080/servicemodelsamples/service/"  
                binding="wsHttpBinding"  
                contract="*" />  
    </client>  
    <routing>  
      <!-- use the namespace table element to define a prefix for our custom namespace-->  
      <namespaceTable>  
        <add prefix="custom" namespace="http://my.custom.namespace/"/>  
      </namespaceTable>  
      <filters>  
        <!--define the different message filters-->  
        <!--define an xpath message filter to look for the  
            custom header containing a value of 2-->  
        <filter name="XPathFilterRegular" filterType="XPath"  
                filterData="sm:header()/custom:CalcVer = '2'"/>  
        <!--define an xpath message filter to look for the  
            custom header containing a value of 1-->  
        <filter name="XPathFilterRounding" filterType="XPath"  
                filterData="sm:header()/custom:CalcVer = '1'"/>  
        <!--define an xpath message filter to look for  
            messages that do not contain the custom header-->  
        <filter name="XPathFilterNoHeader" filterType="XPath"  
                filterData="count(sm:header()/custom:CalcVer)=0"/>  
      </filters>  
      <filterTables>  
        <filterTable name="filterTable1">  
            <!--add the filters to the message filter table-->  
            <!--look for the custom header = 1, and if we find it,  
                send the message to the rounding calc endpoint-->  
            <add filterName="XPathFilterRounding" endpointName="roundingCalcEndpoint"/>  
            <!--look for the custom header = 2, and if we find it,  
                send the message to the rounding calc endpoint-->  
            <add filterName="XPathFilterRegular" endpointName="regularCalcEndpoint"/>  
            <!--look for the absence of the custom header, and if  
                it is not present, assume the v1 endpoint-->  
            <add filterName="XPathFilterNoHeader" endpointName="roundingCalcEndpoint"/>  
        </filterTable>  
      </filterTables>  
    </routing>  
  </system.serviceModel>  
</configuration>  

EsempioExample

Il codice seguente costituisce un elenco completo dell'applicazione client.The following is a complete listing of the client application.

using System;  
using System.ServiceModel;  
using System.ServiceModel.Channels;  

namespace Microsoft.Samples.AdvancedFilters  
{  
    //The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.  

    //Client implementation code.  
    class Client  
    {  
        static void Main()  
        {  
            //Print out the welcome text  
            Console.WriteLine("This sample routes the Calculator Sample through the new WCF RoutingService");  
            Console.WriteLine("Wait for all the services to indicate that they've started, then press");  
            Console.WriteLine("<ENTER> to start the client.");  

            while (Console.ReadLine() != "quit")  
            {  
                //Offer the Address configuration for the client  
                Console.WriteLine("");  
                Console.WriteLine("Welcome to the Calculator Client!");  

                EndpointAddress epa;  
                //set the default address as the general router endpoint  
                epa = new EndpointAddress("http://localhost/routingservice/router/calculator");  

                //set up the CalculatorClient with the EndpointAddress, the WSHttpBinding, and the ICalculator contract  
                //We use the WSHttpBinding so that the outgoing has a message envelope, which we'll manipulate in a minute  
                CalculatorClient client = new CalculatorClient(new WSHttpBinding(), epa);  
                //client.Endpoint.Contract = ContractDescription.GetContract(typeof(ICalculator));  

                //Ask the customer if they want to add a custom header to the outgoing message.  
                //The Router will look for this header, and if so ignore the endpoint the message was  
                //received on, and instead direct the message to the RoundingCalcService.  
                Console.WriteLine("");  
                Console.WriteLine("Which calculator service should be used?");  
                Console.WriteLine("Enter 1 for the rounding calculator, 2 for the regular calculator.");  
                Console.WriteLine("[1] or [2]?");  

                string header = Console.ReadLine();  

                //get the current operationContextScope from the client's inner channel  
                using (OperationContextScope ocs = new OperationContextScope((client.InnerChannel)))  
                {  
                    //get the outgoing message headers element (collection) from the context  
                    MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders;  

                    //if they wanted to create the header, go ahead and add it to the outgoing message  
                    if (header != null && (header=="1" || header=="2"))  
                    {  
                        //create a new header "RoundingCalculator", no specific namespace, and set the value to   
                        //the value of header.  
                        //the Routing Service will look for this header in order to determine if the message  
                        //should be routed to the RoundingCalculator  
                        messageHeadersElement.Add(MessageHeader.CreateHeader("CalcVer", "http://my.custom.namespace/", header));  
                    }  
                    else //incorrect choice, no header added  
                    {  
                        Console.WriteLine("Incorrect value entered, not adding a header");  
                    }  

                        //call the client operations  
                        CallClient(client);  
                }  

                //close the client to clean it up  
                client.Close();  
                Console.WriteLine();  
                Console.WriteLine("Press <ENTER> to run the client again or type 'quit' to quit.");  
            }  
        }  

        private static void CallClient(CalculatorClient client)  
        {  
            Console.WriteLine("");  
            Console.WriteLine("Sending!");  
            // Call the Add service operation.  
            double value1 = 100.00D;  
            double value2 = 15.99D;  
            double result = client.Add(value1, value2);  
            Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);  

            // Call the Subtract service operation.  
            value1 = 145.00D;  
            value2 = 76.54D;  
            result = client.Subtract(value1, value2);  
            Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);  

            // Call the Multiply service operation.  
            value1 = 9.00D;  
            value2 = 81.25D;  
            result = client.Multiply(value1, value2);  
            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);  

            // Call the Divide service operation.  
            value1 = 22.00D;  
            value2 = 7.00D;  
            result = client.Divide(value1, value2);  
            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);  

        }  
    }  
}  

Vedere ancheSee Also

Servizi di routingRouting Services