Dela via


Transport: Anpassade transaktioner via UDP-exempel

Exemplet TransactionMessagePropertyUDPTransport baseras på exemplet Transport: UDP i Windows Communication Foundation (WCF)Transport Extensibility. Det utökar UDP Transport-exemplet för att stödja anpassat transaktionsflöde och visar användningen av TransactionMessageProperty egenskapen.

Kodändringar i UDP-transportexemplet

För att demonstrera transaktionsflödet ändrar exemplet tjänstkontraktet för för ICalculatorContract att kräva ett transaktionsomfång för CalculatorService.Add(). Exemplet lägger också till en extra System.Guid parameter i kontraktet för åtgärden Add . Den här parametern används för att skicka identifieraren för klienttransaktionen till tjänsten.

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

    [...]
}

Exemplet Transport: UDP använder UDP-paket för att skicka meddelanden mellan en klient och en tjänst. Exemplet Transport: Custom Transport använder samma mekanism för att transportera meddelanden, men när en transaktion flödas infogas den i UDP-paketet tillsammans med det kodade meddelandet.

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

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

TransactionMessageBuffer.WriteTransactionMessageBuffer är en hjälpmetod som innehåller nya funktioner för att sammanfoga spridningstoken för den aktuella transaktionen med meddelandeentiteten och placera den i en buffert.

För anpassad transaktionsflödestransport måste klientimplementeringen veta vilka tjänståtgärder som kräver transaktionsflöde och skicka den här informationen till WCF. Det bör också finnas en mekanism för att överföra användartransaktionen till transportskiktet. Det här exemplet använder "WCF-meddelandekontrollanter" för att hämta den här informationen. Klientmeddelandekontrollen som implementeras här, som kallas TransactionFlowInspector, utför följande uppgifter:

  • Avgör om en transaktion måste flödas för en viss meddelandeåtgärd (detta sker i IsTxFlowRequiredForThisOperation()).

  • Kopplar den aktuella omgivande transaktionen till meddelandet med , TransactionFlowPropertyom en transaktion måste skickas (detta görs i 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.
      [...]
 }
}

Själv TransactionFlowInspector skickas till ramverket med ett anpassat beteende: 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)
      {
      }
}

Med den föregående mekanismen på plats skapar användarkoden en TransactionScope innan du anropar tjänståtgärden. Meddelandekontrollen ser till att transaktionen skickas till transporten om den måste skickas till tjänståtgärden.

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

När du tar emot ett UDP-paket från klienten deserialiserar tjänsten det för att extrahera meddelandet och eventuellt en transaktion.

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

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

TransactionMessageBuffer.ReadTransactionMessageBuffer() är den hjälpmetod som vänder serialiseringsprocessen som utförs av TransactionMessageBuffer.WriteTransactionMessageBuffer().

Om en transaktion flödades in läggs den till i meddelandet i TransactionMessageProperty.

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

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

Detta säkerställer att avsändaren hämtar transaktionen vid leveranstillfället och använder den när den anropar den tjänståtgärd som hanteras av meddelandet.

Så här konfigurerar du, skapar och kör exemplet

  1. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

  2. Det aktuella exemplet ska köras på samma sätt som exemplet Transport: UDP . Starta tjänsten med UdpTestService.exe för att köra den. Om du kör Windows Vista måste du starta tjänsten med utökade privilegier. Det gör du genom att högerklicka på UdpTestService.exe i Utforskaren och klicka på Kör som administratör.

  3. Detta ger följande utdata.

    Testing Udp From Code.
    Service is started from code...
    Press <ENTER> to terminate the service and start service from config...
    
  4. Just nu kan du starta klienten genom att köra UdpTestClient.exe. Utdata som genereras av klienten är följande.

    0
    3
    6
    9
    12
    Press <ENTER> to complete test.
    
  5. Tjänstens utdata är följande.

    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. Tjänstprogrammet visar meddelandet The client transaction has flowed to the service om det kan matcha transaktionsidentifieraren som skickas av klienten, i parametern clientTransactionIdCalculatorService.Add() för åtgärden, till identifieraren för tjänsttransaktionen. En matchning erhålls endast om klienttransaktionen har flödat till tjänsten.

  7. Om du vill köra klientprogrammet mot slutpunkter som publicerats med hjälp av konfiguration trycker du på RETUR i fönstret för tjänstprogram och kör sedan testklienten igen. Du bör se följande utdata på tjänsten.

    Testing Udp From Config.
    Service is started from config...
    Press <ENTER> to terminate the service and exit...
    
  8. När klienten körs mot tjänsten genereras nu liknande utdata som tidigare.

  9. Om du vill återskapa klientkoden och konfigurationen med hjälp av Svcutil.exe startar du tjänstprogrammet och kör sedan följande Svcutil.exe kommando från rotkatalogen i exemplet.

    svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
    
  10. Observera att Svcutil.exe inte genererar bindningstilläggskonfigurationen sampleProfileUdpBindingför . Du måste lägga till den manuellt.

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

Se även