Transporte: Transações personalizadas sobre exemplo UDP
O exemplo TransactionMessagePropertyUDPTransport é baseado no exemplo Transport: UDP na extensibilidade de transporte do Windows Communication Foundation (WCF). Ele estende o exemplo de Transporte UDP para dar suporte ao fluxo de transação personalizado e demonstra o TransactionMessageProperty uso da propriedade.
Alterações de código no exemplo de transporte UDP
Para demonstrar o fluxo de transações, o exemplo altera o contrato de serviço para ICalculatorContract
exigir um escopo de transação para CalculatorService.Add()
. O exemplo também adiciona um parâmetro extra System.Guid
ao contrato da Add
operação. Este parâmetro é usado para passar o identificador da transação do cliente para o serviço.
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);
}
[...]
}
O exemplo Transport: UDP usa pacotes UDP para passar mensagens entre um cliente e um serviço. O Transport: Custom Transport Sample usa o mesmo mecanismo para transportar mensagens, mas quando uma transação é fluída, ela é inserida no pacote UDP junto com a mensagem codificada.
byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);
int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);
TransactionMessageBuffer.WriteTransactionMessageBuffer
é um método auxiliar que contém nova funcionalidade para mesclar o token de propagação da transação atual com a entidade de mensagem e colocá-lo em um buffer.
Para o transporte de fluxo de transação personalizado, a implementação do cliente deve saber quais operações de serviço exigem fluxo de transação e passar essas informações para o WCF. Também deve haver um mecanismo para transmitir a transação do usuário para a camada de transporte. Este exemplo usa "inspetores de mensagens WCF" para obter essas informações. O inspetor de mensagens do cliente implementado aqui, que é chamado TransactionFlowInspector
, executa as seguintes tarefas:
Determina se uma transação deve ser fluída para uma determinada ação de mensagem (isso ocorre em
IsTxFlowRequiredForThisOperation()
).Anexa a transação ambiente atual à mensagem usando , se
TransactionFlowProperty
for necessário que uma transação seja fluída (isso é feito emBeforeSendRequest()
).
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.
[...]
}
}
O TransactionFlowInspector
próprio é passado para a estrutura usando um comportamento personalizado: o 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)
{
}
}
Com o mecanismo anterior em vigor, o código do usuário cria uma TransactionScope
operação antes de chamar o serviço. O inspetor de mensagens garante que a transação seja passada para o transporte, caso seja necessário fluir para a operação de serviço.
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;
}
Ao receber um pacote UDP do cliente, o serviço o desserializa para extrair a mensagem e, possivelmente, uma transação.
count = listenSocket.EndReceiveFrom(result, ref dummy);
// read the transaction and message TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);
TransactionMessageBuffer.ReadTransactionMessageBuffer()
é o método auxiliar que reverte o processo de serialização executado pelo TransactionMessageBuffer.WriteTransactionMessageBuffer()
.
Se uma transação foi fluída, ela é anexada à mensagem no TransactionMessageProperty
.
message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);
if (transaction != null)
{
TransactionMessageProperty.Set(transaction, message);
}
Isso garante que o dispatcher pegue a transação no momento do despacho e a use ao chamar a operação de serviço endereçada pela mensagem.
Para configurar, compilar e executar o exemplo
Para criar a solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
O exemplo atual deve ser executado de forma semelhante ao exemplo Transport: UDP . Para executá-lo, inicie o serviço com UdpTestService.exe. Se você estiver executando o Windows Vista, você deve iniciar o serviço com privilégios elevados. Para fazer isso, clique com o botão direito do mouse em UdpTestService.exe no Explorador de Arquivos e clique em Executar como administrador.
Isso produz a seguinte saída.
Testing Udp From Code. Service is started from code... Press <ENTER> to terminate the service and start service from config...
Neste momento, você pode iniciar o cliente executando UdpTestClient.exe. A saída produzida pelo cliente é a seguinte.
0 3 6 9 12 Press <ENTER> to complete test.
A saída do serviço é a seguinte.
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
O aplicativo de serviço exibe a mensagem
The client transaction has flowed to the service
se puder corresponder ao identificador de transação enviado pelo cliente, noclientTransactionId
parâmetro daCalculatorService.Add()
operação, ao identificador da transação de serviço. Uma correspondência é obtida somente se a transação do cliente tiver fluído para o serviço.Para executar o aplicativo cliente em pontos de extremidade publicados usando a configuração, pressione ENTER na janela do aplicativo de serviço e execute o cliente de teste novamente. Você deve ver a seguinte saída no serviço.
Testing Udp From Config. Service is started from config... Press <ENTER> to terminate the service and exit...
A execução do cliente no serviço agora produz uma saída semelhante à anterior.
Para regenerar o código do cliente e a configuração usando Svcutil.exe, inicie o aplicativo de serviço e execute o seguinte comando Svcutil.exe a partir do diretório raiz do exemplo.
svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
Observe que Svcutil.exe não gera a configuração de extensão de vinculação para o
sampleProfileUdpBinding
, você deve adicioná-la manualmente.<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>