Transport: Beispiel für benutzerdefinierte Transaktionen über UDPTransport: Custom Transactions over UDP Sample

Dieses Beispiel basiert auf der Transport: UDP -Beispiel in der Windows Communication Foundation (WCF)Windows Communication Foundation (WCF) Transporterweiterbarkeit.This sample is based on the Transport: UDP sample in the Windows Communication Foundation (WCF)Windows Communication Foundation (WCF)Transport Extensibility. Es erweitert das Beispiel für den UDP-Transport, um einen benutzerdefinierten Transaktionsfluss zu unterstützen, und veranschaulicht die Verwendung der TransactionMessageProperty-Eigenschaft.It extends the UDP Transport sample to support custom transaction flow and demonstrates the use of the TransactionMessageProperty property.

Codeänderungen im Beispiel für den UDP-TransportCode Changes in the UDP Transport Sample

Zur Veranschaulichung des Transaktionsflusses wird der Dienstvertrag für ICalculatorContract im Beispiel geändert, sodass für CalculatorService.Add() ein Transaktionsbereich erforderlich ist.To demonstrate transaction flow, the sample changes the service contract for ICalculatorContract to require a transaction scope for CalculatorService.Add(). Im Beispiel wird dem Vertrag des System.Guid-Vorgangs außerdem ein zusätzlicher Add-Parameter hinzugefügt.The sample also adds an extra System.Guid parameter to the contract of the Add operation. Dieser Parameter wird dazu verwendet, den Bezeichner der Clienttransaktion an den Dienst zu übergeben.This parameter is used to pass the identifier of the client transaction to the 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);  
    }  

    [...]  
}  

Die Transport: UDP Beispiel verwendet die UDP-Pakete, um Nachrichten zwischen einem Client und einem Dienst zu übergeben.The Transport: UDP sample uses UDP packets to pass messages between a client and a service. Die Transport: benutzerdefinierte Transportbeispiel verwendet denselben Mechanismus zum Transportieren von Nachrichten, aber wenn eine Transaktion übergeben wird, wird Sie in den UDP-Pakets zusammen mit der codierten Nachricht eingefügt.The Transport: Custom Transport Sample uses the same mechanism to transport messages, but when a transaction is flowed, it is inserted into the UDP packet along with the encoded message.

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

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

Bei TransactionMessageBuffer.WriteTransactionMessageBuffer handelt es sich um eine Hilfsmethode mit einer neuen Funktionalität, mit der das Weitergabetoken für die aktuelle Transaktion mit der Nachrichtenentität zusammengeführt und in einem Puffer platziert wird.TransactionMessageBuffer.WriteTransactionMessageBuffer is a helper method that contains new functionality to merge the propagation token for the current transaction with the message entity and place it into a buffer.

Zur Übertragung über einen benutzerdefinierten Transaktionsfluss muss die Clientimplementierung wissen, welche Dienstvorgänge einen Transaktionsfluss erfordern, und diese Information an WCFWCF übergeben.For custom transaction flow transport, the client implementation must know what service operations require transaction flow and to pass this information to WCFWCF. Es sollte auch ein Mechanismus zum Senden der Benutzertransaktion an die Transportebene vorhanden sein.There should also be a mechanism for transmitting the user transaction to the transport layer. In diesem Beispiel werden "WCFWCF-Nachrichteninspektoren" verwendet, um diese Informationen abzurufen.This sample uses "WCFWCF message inspectors" to obtain this information. Der hier implementierte Clientnachrichteninspektor mit der Bezeichnung TransactionFlowInspector führt die folgenden Aufgaben aus:The client message inspector implemented here, which is called TransactionFlowInspector, performs the following tasks:

  • Bestimmt, ob für eine bestimmte Nachrichtenaktion ein Transaktionsfluss erfolgen muss (dies geschieht in IsTxFlowRequiredForThisOperation()).Determines whether a transaction must be flowed for a given message action (this takes place in IsTxFlowRequiredForThisOperation()).

  • Fügt die aktuelle Ambient-Transaktion mithilfe von TransactionFlowProperty an die Nachricht an, falls ein Transaktionsfluss erfolgen muss (dies geschieht in BeforeSendRequest()).Attaches the current ambient transaction to the message using TransactionFlowProperty, if a transaction is required to be flowed (this is done 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.  
      [...]  
 }  
}  

Der TransactionFlowInspector selbst wird mithilfe eines benutzerdefinierten Verhaltens (TransactionFlowBehavior) ans Framework übergeben.The TransactionFlowInspector itself is passed to the framework using a custom behavior: the 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)  
      {  
      }  
}  

Wenn der vorangehende Mechanismus vorhanden ist, erstellt der Benutzercode vor dem Aufrufen des Dienstvorgangs einen TransactionScope.With the preceding mechanism in place, the user code creates a TransactionScope before calling the service operation. Der Nachrichteninspektor stellt sicher, dass die Transaktion an den Transport übergeben wird, falls sie an den Dienstvorgang übergeben werden muss.The message inspector ensures that the transaction is passed to the transport in case it is required to be flowed to the service operation.

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

Nach dem Empfang eines UDP-Pakets vom Client wird dieses vom Dienst deserialisiert, um die Nachricht und möglicherweise eine Transaktion zu extrahieren.Upon receiving a UDP packet from the client, the service deserializes it to extract the message and possibly a transaction.

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

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

TransactionMessageBuffer.ReadTransactionMessageBuffer() ist die Hilfsmethode, die den von TransactionMessageBuffer.WriteTransactionMessageBuffer() ausgeführten Serialisierungsprozess umkehrt.TransactionMessageBuffer.ReadTransactionMessageBuffer() is the helper method that reverses the serialization process performed by TransactionMessageBuffer.WriteTransactionMessageBuffer().

Wenn ein Transaktionsfluss erfolgte, wird die Transaktion an die Nachricht in TransactionMessageProperty angehängt.If a transaction was flowed in, it is appended to the message in the TransactionMessageProperty.

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

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

Auf diese Weise wird sichergestellt, dass der Verteiler die Transaktion zum Versandzeitpunkt aufnimmt und sie beim Aufrufen des Dienstvorgangs verwendet, an den die Nachricht adressiert ist.This ensures that the dispatcher picks up the transaction at dispatch time and uses it when calling the service operation addressed by the message.

So können Sie das Beispiel einrichten, erstellen und ausführenTo set up, build, and run the sample

  1. Führen Sie zum Erstellen der Projektmappe die Anweisungen im Erstellen der Windows Communication Foundation-Beispiele.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  2. Im aktuellen Beispiel sollte ähnlich wie ausgeführt werden die Transport: UDP Beispiel.The current sample should be run similarly to the Transport: UDP sample. Starten Sie den Dienst mit UdpTestService.exe, um das Beispiel auszuführen.To run it, start the service with UdpTestService.exe. Wenn Sie Windows VistaWindows Vista ausführen, müssen Sie den Dienst mit erhöhten Rechten starten.If you are running Windows VistaWindows Vista, you must start the service with elevated privileges. Zu diesem Zweck Maustaste UdpTestService.exe in Datei-ExplorerFile Explorer , und klicken Sie auf als Administrator ausführen.To do so, right-click UdpTestService.exe in Datei-ExplorerFile Explorer and click Run as administrator.

  3. Hierdurch wird die folgende Ausgabe generiert.This produces the following output.

    Testing Udp From Code.  
    Service is started from code...  
    Press <ENTER> to terminate the service and start service from config...  
    
  4. Zu diesem Zeitpunkt können Sie den Client durch Ausführen von UdpTestClient.exe starten.At this time, you can start the client by running UdpTestClient.exe. Die vom Client erzeugte Ausgabe lautet wie folgt.The output produced by the client is as follows.

    0  
    3  
    6  
    9  
    12  
    Press <ENTER> to complete test.  
    
  5. Die Ausgabe des Diensts lautet wie folgt.The service output is as follows.

    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. Die Dienstanwendung zeigt die Nachricht The client transaction has flowed to the service an, wenn die vom Client gesendete Transaktions-ID (im clientTransactionId-Parameter des Vorgangs CalculatorService.Add()) mit der ID der Diensttransaktion übereinstimmt.The service application displays the message The client transaction has flowed to the service if it can match the transaction identifier sent by the client, in the clientTransactionId parameter of the CalculatorService.Add() operation, to the identifier of the service transaction. Eine Übereinstimmung liegt nur dann vor, wenn die Clienttransaktion an den Dienst übergeben wurde.A match is obtained only if the client transaction has flowed to the service.

  7. Um die Clientanwendung für Endpunkte auszuführen, die mithilfe einer Konfiguration veröffentlicht wurden, drücken Sie die EINGABETASTE im Dienstanwendungsfenster, und führen Sie den Testclient erneut aus.To run the client application against endpoints published using configuration, press ENTER on the service application window and then run the test client again. Auf dem Dienst sollten Sie die folgende Ausgabe erhalten:You should see the following output on the service.

    Testing Udp From Config.  
    Service is started from config...  
    Press <ENTER> to terminate the service and exit...  
    
  8. Das Ausführen des Clients für den Dienst erzeugt nun eine ähnliche Ausgabe wie zuvor.Running the client against the service now produces similar output as before.

  9. Um den Clientcode und die Konfiguration mithilfe von Svcutil.exe neu zu generieren, starten Sie die Dienstanwendung, und führen Sie dann den folgenden Svcutil.exe-Befehl aus dem Stammverzeichnis des Beispiels aus.To regenerate the client code and configuration using Svcutil.exe, start the service application and then run the following Svcutil.exe command from the root directory of the sample.

    svcutil http://localhost:8000/udpsample/ /reference:UdpTranport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config  
    
  10. Beachten Sie, dass Svcutil.exe nicht die Bindungserweiterungskonfiguration für sampleProfileUdpBinding generiert. Sie müssen diese manuell hinzufügen.Note that Svcutil.exe does not generate the binding extension configuration for the sampleProfileUdpBinding; you must add it manually.

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

Wichtig

Die Beispiele sind möglicherweise bereits auf dem Computer installiert.The samples may already be installed on your machine. Suchen Sie nach dem folgenden Verzeichnis (Standardverzeichnis), bevor Sie fortfahren.Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Wenn dieses Verzeichnis nicht vorhanden ist, rufen Sie Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 auf, um alle Windows Communication Foundation (WCF)Windows Communication Foundation (WCF) - und WFWF -Beispiele herunterzuladen.If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF)Windows Communication Foundation (WCF) and WFWF samples. Dieses Beispiel befindet sich im folgenden Verzeichnis.This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Transactions\TransactionMessagePropertyUDPTransport

Siehe auchSee Also

Transport: UDPTransport: UDP