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 inBeforeSendRequest()
).
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
Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.
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.
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...
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.
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
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 declientTransactionId
parameter van deCalculatorService.Add()
bewerking, met de id van de servicetransactie. Er wordt alleen een overeenkomst verkregen als de clienttransactie naar de service is gestroomd.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...
Het uitvoeren van de client op basis van de service produceert nu vergelijkbare uitvoer als voorheen.
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
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>