Delen via


Procedure: Serviceversiebeheer

In dit onderwerp worden de basisstappen beschreven die nodig zijn om een routeringsconfiguratie te maken waarmee berichten worden gerouteerd naar verschillende versies van dezelfde service. In dit voorbeeld worden berichten doorgestuurd naar twee verschillende versies van een rekenmachineservice ( roundingCalc v1) en regularCalc (v2). Beide implementaties ondersteunen dezelfde bewerkingen; met de oudere service worden roundingCalcalle berekeningen echter afgerond op de dichtstbijzijnde gehele waarde voordat ze worden geretourneerd. Een clienttoepassing moet kunnen aangeven of de nieuwere regularCalc service moet worden gebruikt.

Waarschuwing

Als u een bericht wilt routeren naar een specifieke serviceversie, moet de routeringsservice de bestemming van het bericht kunnen bepalen op basis van de inhoud van het bericht. In de onderstaande methode geeft de client de versie op door informatie in een berichtkop in te voegen. Er zijn methoden voor serviceversiebeheer waarvoor clients geen aanvullende gegevens hoeven door te geven. Een bericht kan bijvoorbeeld worden doorgestuurd naar de meest recente of meest compatibele versie van een service of de router kan een deel van de standaard SOAP-envelop gebruiken.

De bewerkingen die door beide services worden weergegeven, zijn:

  • Toevoegen
  • Aftrekken
  • Vermenigvuldigen
  • Delen

Omdat beide service-implementaties dezelfde bewerkingen verwerken en in wezen identiek zijn aan de gegevens die ze retourneren, zijn de basisgegevens die zijn opgenomen in berichten die vanuit clienttoepassingen worden verzonden, niet uniek genoeg, zodat u kunt bepalen hoe de aanvraag moet worden gerouteerd. Actiefilters kunnen bijvoorbeeld niet worden gebruikt omdat de standaardacties voor beide services hetzelfde zijn.

Dit kan op verschillende manieren worden opgelost, zoals het blootstellen van een specifiek eindpunt op de router voor elke versie van de service of het toevoegen van een aangepast header-element aan het bericht om de serviceversie aan te geven. Met elk van deze benaderingen kunt u binnenkomende berichten op unieke wijze routeren naar een specifieke versie van de service, maar het gebruik van unieke berichtinhoud is de voorkeursmethode om onderscheid te maken tussen aanvragen voor verschillende serviceversies.

In dit voorbeeld voegt de clienttoepassing de aangepaste header 'CalcVer' toe aan het aanvraagbericht. Deze header bevat een waarde die de versie van de service aangeeft waarnaar het bericht moet worden doorgestuurd. Een waarde van '1' geeft aan dat het bericht moet worden verwerkt door de roundingCalc-service, terwijl een waarde van '2' de regulierecalc-service aangeeft. Hierdoor kan de clienttoepassing rechtstreeks bepalen welke versie van de service het bericht verwerkt. Omdat de aangepaste header een waarde in het bericht is, kunt u één eindpunt gebruiken om berichten te ontvangen die bestemd zijn voor beide versies van de service. De volgende code kan worden gebruikt in de clienttoepassing om deze aangepaste header toe te voegen aan het bericht:

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

Serviceversiebeheer implementeren

  1. Maak de basisconfiguratie van de routeringsservice door het service-eindpunt op te geven dat door de service wordt weergegeven. In het volgende voorbeeld wordt één service-eindpunt gedefinieerd, dat wordt gebruikt voor het ontvangen van berichten. Het definieert ook de clienteindpunten die worden gebruikt voor het verzenden van berichten naar de roundingCalc (v1) en de 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. Definieer de filters die worden gebruikt om berichten naar de doeleindpunten te routeren. In dit voorbeeld wordt het XPath-filter gebruikt om de waarde van de aangepaste header 'CalcVer' te detecteren om te bepalen naar welke versie het bericht moet worden gerouteerd. Een XPath-filter wordt ook gebruikt voor het detecteren van berichten die geen koptekst 'CalcVer' bevatten. In het volgende voorbeeld worden de vereiste filters en naamruimtetabel gedefinieerd.

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

    Notitie

    Het voorvoegsel voor de s12-naamruimte wordt standaard gedefinieerd in de naamruimtetabel en vertegenwoordigt de naamruimte http://www.w3.org/2003/05/soap-envelope.

  3. Definieer de filtertabel, die elk filter koppelt aan een clienteindpunt. Als het bericht de koptekst 'CalcVer' bevat met een waarde van 1, wordt het verzonden naar de regularCalc-service. Als de header een waarde van 2 bevat, wordt deze verzonden naar de roundingCalc-service. Als er geen koptekst aanwezig is, wordt het bericht doorgestuurd naar de reguliere berekening.

    Hieronder wordt de filtertabel gedefinieerd en worden de eerder gedefinieerde filters toegevoegd.

    <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. Als u binnenkomende berichten wilt evalueren op basis van de filters in de filtertabel, moet u de filtertabel koppelen aan de service-eindpunten met behulp van het routeringsgedrag. In het volgende voorbeeld ziet u hoe u deze aan de service-eindpunten kunt koppelen filterTable1 :

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

Voorbeeld 1

Hier volgt een volledige lijst van het configuratiebestand.

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

Voorbeeld 2

Hier volgt een volledige lijst van de clienttoepassing.

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

        }
    }
}

Zie ook