Транспорт: образец пользовательских транзакций по протоколу UDP
Этот образец основан на образце Транспорт: UDP Transport Extensibility Samples Windows Communication Foundation (WCF). Он расширяет образец транспорта определяемой пользователем процедуры за счет поддержки пользовательского потока транзакций и иллюстрирует использование свойства TransactionMessageProperty.
Изменения кода в образце транспорта UDP
Чтобы продемонстрировать поток транзакций, в этом образце изменен контракт службы для ICalculatorContract
. Здесь этот контракт требует область транзакции для CalculatorService.Add()
. Кроме того, в контракт операции Add
добавлен параметр System.Guid. Этот параметр используется для передачи службе идентификатора транзакции клиента.
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 использует для транспортировки сообщений тот же механизм, но при создании потока транзакции она вставляется в пакет 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;
}
После получения от клиента пакета UPD служба десериализует его, чтобы извлечь сообщение и, возможно, транзакцию.
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.
Этот образец следует выполнять так же, как образец Транспорт: 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 https://localhost:8000/udpsample/ /reference:UdpTranport\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>
![]() |
---|
Образцы уже могут быть установлены на компьютере. Перед продолжением проверьте следующий каталог (по умолчанию).
<диск_установки>:\WF_WCF_Samples
Если этот каталог не существует, перейдите на страницу Образцы Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для .NET Framework 4, чтобы загрузить все образцы Windows Communication Foundation (WCF) и WF. Этот образец расположен в следующем каталоге.
<диск_установки>:\WF_WCF_Samples\WCF\Extensibility\Transactions\TransactionMessagePropertyUDPTransport
|