Transport: Custom Transactions over UDP Sample
Cet exemple repose sur l'exemple Transport: UDP de la rubrique Windows Communication Foundation (WCF) Exemples d'extensibilité du transport. Il étend l'exemple de transport UDP afin de prendre en charge le flux de transactions personnalisé et présente l'utilisation de la propriété TransactionMessageProperty.
Remarque : |
---|
Cet exemple requiert l'installation de .NET Framework version 3.5 pour être généré et exécuté. Visual Studio 2008 est nécessaire pour l'ouverture des fichiers projet et solution. |
Modifications du code dans l'exemple UDP Transport
Pour illustrer le flux de transactions, l'exemple modifie le contrat de service de ICalculatorContract
afin qu'une portée de transaction soit requise pour CalculatorService.Add()
. L'exemple ajoute également un paramètre System.Guid supplémentaire au contrat de l'opération Add
. Ce paramètre permet de transmettre l'identificateur de la transaction cliente au 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);
}
[...]
}
L'exemple Transport: UDP utilise des paquets UDP pour transmettre des messages entre un client et un service. Transport: Custom Transactions over UDP Sample utilise le même mécanisme pour transporter des messages, mais lorsqu'une transaction est transmise, elle est insérée dans le paquet UDP avec le message codé.
byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);
int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);
TransactionMessageBuffer.WriteTransactionMessageBuffer
est une méthode d'assistance qui contient de nouvelles fonctionnalités permettant de fusionner le jeton de propagation de la transaction actuelle avec l'entité de message, et de le placer dans une mémoire tampon.
Pour le transport du flux de transactions personnalisé, l'implémentation du client doit connaître les opérations de service qui requièrent le flux de transactions et transmettre ces informations à WCF. Un mécanisme doit également permettre de transmettre la transaction utilisateur à la couche de transport. Cet exemple utilise des « inspecteurs de message WCF » pour obtenir ces informations. L'inspecteur de message client implémenté ici et appelé TransactionFlowInspector
effectue les tâches suivantes :
- Il détermine si une transaction doit être transmise pour une action de message donnée (cette opération a lieu dans
IsTxFlowRequiredForThisOperation()
). - Il joint la transaction ambiante actuelle au message à l'aide de
TransactionFlowProperty
, si une transaction doit être transmise (cette opération s'effectue dansBeforeSendRequest()
).
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.
[...]
}
}
Le TransactionFlowInspector
lui-même est passé à l'infrastructure à l'aide d'un comportement personnalisé : 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)
{
}
}
Le mécanisme précédent étant en place, le code utilisateur crée un TransactionScope
avant d'appeler l'opération de service. L'inspecteur de message veille à ce que la transaction soit passée au transport si elle doit être transmise à l'opération de service.
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;
}
Après réception d'un paquet UDP émanant du client, le service désérialise ce paquet afin d'extraire le message et éventuellement une transaction.
count = listenSocket.EndReceiveFrom(result, ref dummy);
// read the transaction and message TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);
TransactionMessageBuffer.ReadTransactionMessageBuffer()
est la méthode d'assistance qui annule le processus de sérialisation effectué par TransactionMessageBuffer.WriteTransactionMessageBuffer()
.
Si une transaction a été transmise, elle est ajoutée au message dans TransactionMessageProperty
.
message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);
if (transaction != null)
{
TransactionMessageProperty.Set(transaction, message);
}
Cela garantit que le répartiteur sélectionne la transaction au moment de la distribution et l'utilise lors de l'appel de l'opération de service traitée par le message.
Pour configurer, générer et exécuter l'exemple
Pour générer la solution, suivez les instructions indiquées dans Génération des exemples Windows Communication Foundation.
L'exemple actuel doit être exécuté de la même façon que l'exemple Transport: UDP. Pour l'exécuter, démarrez le service avec UdpTestService.exe. Si vous exécutez Windows Vista, vous devez démarrer le service avec des privilèges élevés. Pour ce faire, cliquez avec le bouton droit sur UdpTestService.exe dans l'Explorateur Windows, puis cliquez sur Exécuter en tant qu'administrateur.
La sortie suivante est alors générée.
Testing Udp From Code. Service is started from code... Press <ENTER> to terminate the service and start service from config...
À ce stade, vous pouvez démarrer le client en exécutant UdpTestClient.exe. La sortie générée par le client se présente comme suit.
0 3 6 9 12 Press <ENTER> to complete test.
La sortie du service se présente comme suit.
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
L'application de service affiche le message
The client transaction has flowed to the service
si elle peut faire correspondre l'identificateur de transaction envoyé par le client (dans le paramètreclientTransactionId
de l'opérationCalculatorService.Add()
) à l'identificateur de la transaction de service. Une correspondance est obtenue uniquement si la transaction cliente est transmise au service.Pour exécuter l'application cliente sur des points de terminaison publiés à l'aide de la configuration, appuyez sur ENTRÉE dans la fenêtre d'application de service, puis réexécutez le client test. La sortie suivante doit s'afficher sur le service.
Testing Udp From Config. Service is started from config... Press <ENTER> to terminate the service and exit...
L'exécution du client sur le service génère maintenant une sortie similaire à la précédente.
Pour régénérer la configuration et le code client à l'aide de Svcutil.exe, démarrez l'application de service, puis exécutez la commande Svcutil.exe suivante à partir du répertoire racine de l'exemple.
svcutil https://localhost:8000/udpsample/ /reference:UdpTranport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
Svcutil.exe ne générant pas de configuration d'extension de liaison pour
sampleProfileUdpBinding
, vous devez donc l'ajouter manuellement.<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>
Voir aussi
Tâches
Send comments about this topic to Microsoft.
© 2007 Microsoft Corporation. All rights reserved.