Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Пример TransactionMessagePropertyUDPTransport основан на примере Транспорт: UDP в Windows Communication Foundation (WCF)Transport Extensibility. Он расширяет пример транспорта UDP для поддержки пользовательского потока транзакций и демонстрирует использование свойства TransactionMessageProperty.
Изменения кода в примере транспорта UDP
Для демонстрации потока транзакций образец изменяет контракт службы ICalculatorContract
, чтобы требовать область транзакции для CalculatorService.Add()
. В примере также добавляется дополнительный System.Guid
параметр в контракт Add
операции. Этот параметр используется для передачи идентификатора клиентской транзакции службе.
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);
}
[...]
}
Пример использования протокола UDP демонстрирует передачу сообщений с помощью пакетов UDP между клиентом и сервером. Транспорт: Образец пользовательского транспорта использует тот же механизм для транспортировки сообщений, но когда транзакция передается, она вставляется в пакет UDP вместе с закодированным сообщением.
byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);
int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);
TransactionMessageBuffer.WriteTransactionMessageBuffer
— вспомогательный метод, который содержит новые функциональные возможности для слияния маркера распространения для текущей транзакции с сущностью сообщения и помещает его в буфер.
Для пользовательского транспорта потока транзакций реализация клиента должна знать, какие операции службы требуют потока транзакций и передавать эти сведения в WCF. Также должен быть механизм передачи транзакции пользователя на транспортный уровень. В этом примере используются инспекторы сообщений WCF для получения этой информации. Инспектор сообщений клиента, реализованный здесь, который вызывается TransactionFlowInspector
, выполняет следующие задачи:
Определяет, должна ли транзакция передаваться для действия заданного сообщения (это происходит в
IsTxFlowRequiredForThisOperation()
).Присоединяет текущую внешнюю транзакцию к сообщению с помощью ,
TransactionFlowProperty
если транзакция требуется передавать (это делается в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.
[...]
}
}
Сам объект TransactionFlowInspector
передается в фреймворк с помощью пользовательского поведения: 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)
{
}
}
При наличии предыдущего механизма код пользователя создает TransactionScope
перед вызовом операции сервиса. Инспектор сообщений гарантирует, что транзакция передается транспорту, если требуется передача в операцию службы.
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;
}
Получив пакет UDP от клиента, служба десериализирует его, чтобы извлечь сообщение и, возможно, транзакцию.
count = listenSocket.EndReceiveFrom(result, ref dummy);
// read the transaction and message TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);
TransactionMessageBuffer.ReadTransactionMessageBuffer()
— вспомогательный метод, который изменяет процесс сериализации, выполняемый TransactionMessageBuffer.WriteTransactionMessageBuffer()
.
Если транзакция была выполнена, она добавляется к сообщению в объекте TransactionMessageProperty
.
message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);
if (transaction != null)
{
TransactionMessageProperty.Set(transaction, message);
}
Это гарантирует, что диспетчер получает транзакцию во время отправки и использует ее при вызове операции службы, адресованной сообщению.
Настройка, сборка и запуск примера
Чтобы создать решение, следуйте инструкциям по созданию примеров Windows Communication Foundation.
Текущий образец должен запускаться аналогично образцу Transport: UDP. Чтобы запустить ее, запустите службу с UdpTestService.exe. Если вы работаете под управлением Windows Vista, необходимо запустить службу с повышенными привилегиями. Для этого щелкните правой кнопкой мыши UdpTestService.exe в проводнике и нажмите кнопку "Запуск от имени администратора".
При этом выводятся следующие выходные данные.
Testing Udp From Code. Service is started from code... Press <ENTER> to terminate the service and start service from config...
В настоящее время вы можете запустить клиент, выполнив UdpTestClient.exe. Выходные данные, созданные клиентом, приведены следующим образом.
0 3 6 9 12 Press <ENTER> to complete test.
Выходные данные службы приведены следующим образом.
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
Приложение-служба отображает сообщение
The client transaction has flowed to the service
, если удается сопоставить идентификатор транзакции, отправленный клиентом, в параметреclientTransactionId
операцииCalculatorService.Add()
, с идентификатором транзакции службы. Совпадение обнаруживается только в том случае, если клиентская транзакция дошла до службы.Чтобы запустить клиентское приложение для конечных точек, опубликованных с помощью конфигурации, нажмите клавишу ВВОД в окне приложения службы, а затем снова запустите тестовый клиент. В службе должны появиться следующие выходные данные.
Testing Udp From Config. Service is started from config... Press <ENTER> to terminate the service and exit...
Запуск клиента против службы теперь выдает аналогичные выходные данные, как прежде.
Чтобы повторно создать код клиента и конфигурацию с помощью Svcutil.exe, запустите приложение-службу и выполните следующую команду Svcutil.exe из корневого каталога примера.
svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
Обратите внимание, что Svcutil.exe не создает конфигурацию расширения привязки для
sampleProfileUdpBinding
; его нужно добавить вручную.<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>