Postupy: Verze služby

Toto téma popisuje základní kroky potřebné k vytvoření konfigurace směrování, která směruje zprávy do různých verzí stejné služby. V tomto příkladu se zprávy směrují do dvou různých verzí služby roundingCalc kalkulačky (v1) a regularCalc (v2). Obě implementace podporují stejné operace; starší služba roundingCalcvšak před vrácením zaokrouhlí všechny výpočty na nejbližší celočíselnou hodnotu. Klientská aplikace musí být schopná určit, jestli se má používat novější regularCalc služba.

Upozorňující

Aby bylo možné směrovat zprávu na konkrétní verzi služby, musí být směrovací služba schopná určit cíl zprávy na základě obsahu zprávy. V níže uvedené metodě klient určí verzi vložením informací do hlavičky zprávy. Existují metody správy verzí služby, které nevyžadují, aby klienti předali další data. Například zprávu lze směrovat na nejnovější nebo nejkomppatibilnější verzi služby nebo směrovač může použít část standardní obálky SOAP.

Operace vystavené oběma službami jsou:

  • Přidání
  • Subtract
  • Krát
  • Vydělit

Vzhledem k tomu, že obě implementace služby zpracovávají stejné operace a jsou v podstatě totožné s daty, která vrací, nejsou základní data obsažená ve zprávách odeslaných z klientských aplikací dostatečně jedinečná, aby bylo možné určit, jak směrovat požadavek. Filtry akcí se například nedají použít, protože výchozí akce pro obě služby jsou stejné.

Dá se to vyřešit několika způsoby, například zveřejněním konkrétního koncového bodu ve směrovači pro každou verzi služby nebo přidáním vlastního elementu hlavičky do zprávy označující verzi služby. Každý z těchto přístupů umožňuje jedinečně směrovat příchozí zprávy do konkrétní verze služby, ale využití jedinečného obsahu zpráv je upřednostňovanou metodou odlišování mezi požadavky na různé verze služby.

V tomto příkladu klientská aplikace přidá do zprávy požadavku vlastní hlavičku CalcVer. Tato hlavička bude obsahovat hodnotu, která označuje verzi služby, na kterou má být zpráva směrována. Hodnota 1 označuje, že zpráva musí být zpracována službou RoundingCalc, zatímco hodnota 2 označuje běžnou službuCalc. Klientská aplikace tak může přímo řídit, jakou verzi služby bude zpráva zpracovávat. Vzhledem k tomu, že vlastní hlavička je hodnota obsažená ve zprávě, můžete k příjmu zpráv určených pro obě verze služby použít jeden koncový bod. Následující kód lze v klientské aplikaci použít k přidání této vlastní hlavičky do zprávy:

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

Implementace správy verzí služby

  1. Vytvořte základní konfiguraci služby směrování zadáním koncového bodu služby vystaveného službou. Následující příklad definuje jeden koncový bod služby, který se použije k příjmu zpráv. Definuje také koncové body klienta, které se použijí k odesílání zpráv do roundingCalc služeb (v1) a regularCalc (v2).

    <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. Definujte filtry používané ke směrování zpráv do cílových koncových bodů. V tomto příkladu se filtr XPath používá ke zjištění hodnoty vlastní hlavičky "CalcVer" k určení verze zprávy, do které se má zpráva směrovat. Filtr XPath slouží také k detekci zpráv, které neobsahují hlavičku "CalcVer". Následující příklad definuje požadované filtry a tabulku oboru názvů.

    <!-- 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>
    

    Poznámka:

    Předpona oboru názvů s12 je ve výchozím nastavení definována v tabulce oboru názvů a představuje obor názvů http://www.w3.org/2003/05/soap-envelope.

  3. Definujte tabulku filtrů, která přidruží každý filtr ke koncovému bodu klienta. Pokud zpráva obsahuje hlavičku "CalcVer" s hodnotou 1, odešle se do služby regularCalc. Pokud záhlaví obsahuje hodnotu 2, odešle se službě RoundingCalc. Pokud není k dispozici žádné záhlaví, zpráva bude směrována na regularCalc.

    Následující tabulka definuje tabulku filtrů a přidá filtry definované dříve.

    <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. Pokud chcete vyhodnotit příchozí zprávy proti filtrům obsaženým v tabulce filtru, musíte tabulku filtru přidružit ke koncovým bodům služby pomocí chování směrování. Následující příklad ukazuje přidružení filterTable1 ke koncovým bodům služby:

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

Příklad 1

Následuje úplný seznam konfiguračního souboru.

<?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>

Příklad 2

Následuje úplný seznam klientské aplikace.

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.
                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);

        }
    }
}

Viz také