Delen via


Transport: Aangepaste transacties via UDP-voorbeeld

Het voorbeeld TransactionMessagePropertyUDPTransport is gebaseerd op het voorbeeld Transport: UDP in de Transport Extensibility (WCF)Transport Extensibility van Windows Communication Foundation (WCF). Het breidt het UDP Transport-voorbeeld uit om aangepaste transactiestroom te ondersteunen en demonstreert het gebruik van de TransactionMessageProperty eigenschap.

Codewijzigingen in het UDP-transportvoorbeeld

Om de transactiestroom te demonstreren, wordt in het voorbeeld het servicecontract ICalculatorContract gewijzigd om een transactiebereik voor CalculatorService.Add()te vereisen. In het voorbeeld wordt ook een extra System.Guid parameter toegevoegd aan het contract van de Add bewerking. Deze parameter wordt gebruikt om de id van de clienttransactie door te geven aan de service.

class CalculatorService : IDatagramContract, ICalculatorContract
{
    [OperationBehavior(TransactionScopeRequired=true)]
    public int Add(int x, int y, Guid clientTransactionId)
    {
        if(Transaction.Current.TransactionInformation.DistributedIdentifier == clientTransactionId)
    {
        Console.WriteLine("The client transaction has flowed to the service");
    }
    else
    {
     Console.WriteLine("The client transaction has NOT flowed to the service");
    }

    Console.WriteLine("   adding {0} + {1}", x, y);
    return (x + y);
    }

    [...]
}

Het voorbeeld Transport: UDP maakt gebruik van UDP-pakketten om berichten door te geven tussen een client en een service. Het transport: Custom Transport Sample maakt gebruik van hetzelfde mechanisme voor het transport van berichten, maar wanneer een transactie wordt gestroomd, wordt deze samen met het gecodeerde bericht ingevoegd in het UDP-pakket.

byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);

int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);

TransactionMessageBuffer.WriteTransactionMessageBuffer is een helpermethode die nieuwe functionaliteit bevat om het doorgiftetoken voor de huidige transactie samen te voegen met de berichtentiteit en deze in een buffer te plaatsen.

Voor aangepast transactiestroomtransport moet de client-implementatie weten welke servicebewerkingen transactiestroom vereisen en deze informatie doorgeven aan WCF. Er moet ook een mechanisme zijn voor het verzenden van de gebruikerstransactie naar de transportlaag. In dit voorbeeld wordt WCF-berichtcontrole gebruikt om deze informatie te verkrijgen. De clientberichtcontrole die hier is geïmplementeerd, die wordt aangeroepen TransactionFlowInspector, voert de volgende taken uit:

  • Bepaalt of een transactie moet worden gestroomd voor een bepaalde berichtactie (dit vindt plaats).IsTxFlowRequiredForThisOperation()

  • Koppelt de huidige omgevingstransactie aan het bericht met behulp van TransactionFlowProperty, als een transactie moet worden gestroomd (dit wordt gedaan in BeforeSendRequest()).

public class TransactionFlowInspector : IClientMessageInspector
{
   void IClientMessageInspector.AfterReceiveReply(ref           System.ServiceModel.Channels.Message reply, object correlationState)
  {
  }

   public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
   {
       // obtain the tx propagation token
       byte[] propToken = null;
       if (Transaction.Current != null && IsTxFlowRequiredForThisOperation(request.Headers.Action))
       {
           try
           {
               propToken = TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
           }
           catch (TransactionException e)
           {
              throw new CommunicationException("TransactionInterop.GetTransmitterPropagationToken failed.", e);
           }
       }

      // set the propToken on the message in a TransactionFlowProperty
       TransactionFlowProperty.Set(propToken, request);

       return null;
    }
  }

  static bool IsTxFlowRequiredForThisOperation(String action)
 {
       // In general, this should contain logic to identify which operations (actions)      require transaction flow.
      [...]
 }
}

Het TransactionFlowInspector zelf wordt doorgegeven aan het framework met behulp van een aangepast gedrag: de TransactionFlowBehavior.

public class TransactionFlowBehavior : IEndpointBehavior
{
       public void AddBindingParameters(ServiceEndpoint endpoint,            System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
      {
      }

       public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
      {
            TransactionFlowInspector inspector = new TransactionFlowInspector();
            clientRuntime.MessageInspectors.Add(inspector);
      }

      public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
     {
     }

      public void Validate(ServiceEndpoint endpoint)
      {
      }
}

Met het voorgaande mechanisme maakt de gebruikerscode een TransactionScope voordat de servicebewerking wordt aangeroepen. De berichtcontrole zorgt ervoor dat de transactie wordt doorgegeven aan het transport voor het geval het nodig is om naar de servicebewerking te worden gestroomd.

CalculatorContractClient calculatorClient = new CalculatorContractClient("SampleProfileUdpBinding_ICalculatorContract");
calculatorClient.Endpoint.Behaviors.Add(new TransactionFlowBehavior());

try
{
       for (int i = 0; i < 5; ++i)
      {
              // call the 'Add' service operation under a transaction scope
             using (TransactionScope ts = new TransactionScope())
             {
        [...]
        Console.WriteLine(calculatorClient.Add(i, i * 2));
         }
      }
       calculatorClient.Close();
}
catch (TimeoutException)
{
    calculatorClient.Abort();
}
catch (CommunicationException)
{
    calculatorClient.Abort();
}
catch (Exception)
{
    calculatorClient.Abort();
    throw;
}

Na ontvangst van een UDP-pakket van de client, wordt het door de service gedeserialiseerd om het bericht en eventueel een transactie te extraheren.

count = listenSocket.EndReceiveFrom(result, ref dummy);

// read the transaction and message                       TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);

TransactionMessageBuffer.ReadTransactionMessageBuffer() is de helpermethode waarmee het serialisatieproces wordt omgekeerd dat wordt uitgevoerd door TransactionMessageBuffer.WriteTransactionMessageBuffer().

Als een transactie is gestroomd, wordt deze toegevoegd aan het bericht in de TransactionMessageProperty.

message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);

if (transaction != null)
{
       TransactionMessageProperty.Set(transaction, message);
}

Dit zorgt ervoor dat de dispatcher de transactie ophaalt tijdens de verzending en deze gebruikt bij het aanroepen van de servicebewerking die door het bericht is geadresseerd.

Het voorbeeld instellen, compileren en uitvoeren

  1. Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.

  2. Het huidige voorbeeld moet op dezelfde manier worden uitgevoerd als het voorbeeld Transport: UDP . Als u deze wilt uitvoeren, start u de service met UdpTestService.exe. Als u Windows Vista gebruikt, moet u de service met verhoogde bevoegdheden starten. Klik hiervoor met de rechtermuisknop op UdpTestService.exe in Bestandenverkenner en klik op Als administrator uitvoeren.

  3. Dit produceert de volgende uitvoer.

    Testing Udp From Code.
    Service is started from code...
    Press <ENTER> to terminate the service and start service from config...
    
  4. Op dit moment kunt u de client starten door UdpTestClient.exe uit te voeren. De uitvoer die door de client wordt geproduceerd, is als volgt.

    0
    3
    6
    9
    12
    Press <ENTER> to complete test.
    
  5. De service-uitvoer is als volgt.

    Hello, world!
    Hello, world!
    Hello, world!
    Hello, world!
    Hello, world!
    The client transaction has flowed to the service
       adding 0 + 0
    The client transaction has flowed to the service
       adding 1 + 2
    The client transaction has flowed to the service
       adding 2 + 4
    The client transaction has flowed to the service
       adding 3 + 6
    The client transaction has flowed to the service
       adding 4 + 8
    
  6. De servicetoepassing geeft het bericht The client transaction has flowed to the service weer als deze overeenkomt met de transactie-id die door de client is verzonden, in de clientTransactionId parameter van de CalculatorService.Add() bewerking, met de id van de servicetransactie. Er wordt alleen een overeenkomst verkregen als de clienttransactie naar de service is gestroomd.

  7. Als u de clienttoepassing wilt uitvoeren op eindpunten die zijn gepubliceerd met behulp van de configuratie, drukt u op Enter in het venster van de servicetoepassing en voert u de testclient opnieuw uit. U ziet nu de volgende uitvoer van de service.

    Testing Udp From Config.
    Service is started from config...
    Press <ENTER> to terminate the service and exit...
    
  8. Het uitvoeren van de client op basis van de service produceert nu vergelijkbare uitvoer als voorheen.

  9. Als u de clientcode en configuratie opnieuw wilt genereren met behulp van Svcutil.exe, start u de servicetoepassing en voert u vervolgens de volgende Svcutil.exe opdracht uit vanuit de hoofdmap van het voorbeeld.

    svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
    
  10. Houd er rekening mee dat Svcutil.exe de configuratie van de bindingsextensie niet genereert voor de sampleProfileUdpBinding; u moet deze handmatig toevoegen.

    <configuration>
        <system.serviceModel>
            …
            <extensions>
                <!-- This was added manually because svcutil.exe does not add this extension to the file -->
                <bindingExtensions>
                    <add name="sampleProfileUdpBinding" type="Microsoft.ServiceModel.Samples.SampleProfileUdpBindingCollectionElement, UdpTransport" />
                </bindingExtensions>
            </extensions>
        </system.serviceModel>
    </configuration>
    

Zie ook