Transport: Przykład niestandardowych transakcji przeprowadzanych za pośrednictwem protokołu UDP
Przykład TransactionMessagePropertyUDPTransport jest oparty na przykładzie Transport: UDP w rozszerzalności transportu Windows Communication Foundation (WCF). Rozszerza przykład transportu UDP w celu obsługi niestandardowego TransactionMessageProperty przepływu transakcji i demonstruje użycie właściwości.
Zmiany kodu w przykładzie transportu UDP
Aby zademonstrować przepływ transakcji, przykład zmienia kontrakt usługi, ICalculatorContract
aby wymagać zakresu transakcji dla CalculatorService.Add()
. Przykład dodaje również dodatkowy System.Guid
parametr do kontraktu Add
operacji. Ten parametr służy do przekazywania identyfikatora transakcji klienta do usługi.
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);
}
[...]
}
Przykład Transport: UDP używa pakietów UDP do przekazywania komunikatów między klientem a usługą. Transport: Custom Transport Sample używa tego samego mechanizmu do transportu komunikatów, ale po przepływie transakcji jest on wstawiany do pakietu UDP wraz z zakodowanym komunikatem.
byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);
int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);
TransactionMessageBuffer.WriteTransactionMessageBuffer
to metoda pomocnika zawierająca nowe funkcje scalania tokenu propagacji dla bieżącej transakcji z jednostką komunikatu i umieszczania jej w buforze.
W przypadku transportu niestandardowego przepływu transakcji implementacja klienta musi wiedzieć, jakie operacje usług wymagają przepływu transakcji i przekazać te informacje do programu WCF. Powinien istnieć również mechanizm przesyłania transakcji użytkownika do warstwy transportu. W tym przykładzie użyto "inspektorów komunikatów WCF" w celu uzyskania tych informacji. Inspektor komunikatów klienta zaimplementowany tutaj, który jest nazywany TransactionFlowInspector
, wykonuje następujące zadania:
Określa, czy transakcja musi być przepływana dla danej akcji komunikatu (odbywa się to w pliku
IsTxFlowRequiredForThisOperation()
).Dołącza bieżącą transakcję otoczenia do komunikatu przy użyciu metody
TransactionFlowProperty
, jeśli transakcja jest wymagana do przepływu (odbywa się to w plikuBeforeSendRequest()
).
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.
[...]
}
}
Sam TransactionFlowInspector
element jest przekazywany do platformy przy użyciu niestandardowego zachowania: 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)
{
}
}
Po utworzeniu poprzedniego mechanizmu kod użytkownika tworzy TransactionScope
element przed wywołaniem operacji usługi. Inspektor komunikatów zapewnia, że transakcja jest przekazywana do transportu w przypadku, gdy jest to wymagane do przepływu do operacji usługi.
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;
}
Po otrzymaniu pakietu UDP od klienta usługa deserializuje go w celu wyodrębnienia komunikatu i ewentualnie transakcji.
count = listenSocket.EndReceiveFrom(result, ref dummy);
// read the transaction and message TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);
TransactionMessageBuffer.ReadTransactionMessageBuffer()
to metoda pomocnika, która odwraca proces serializacji wykonywany przez TransactionMessageBuffer.WriteTransactionMessageBuffer()
program .
Jeśli transakcja została przesłana, zostanie ona dołączona do komunikatu w pliku TransactionMessageProperty
.
message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);
if (transaction != null)
{
TransactionMessageProperty.Set(transaction, message);
}
Gwarantuje to, że dyspozytor pobiera transakcję w czasie wysyłki i używa jej podczas wywoływania operacji usługi adresowanej przez komunikat.
Aby skonfigurować, skompilować i uruchomić przykład
Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).
Bieżący przykład powinien być uruchamiany podobnie do przykładu Transport: UDP . Aby go uruchomić, uruchom usługę przy użyciu UdpTestService.exe. Jeśli korzystasz z systemu Windows Vista, musisz uruchomić usługę z podwyższonym poziomem uprawnień. W tym celu kliknij prawym przyciskiem myszy UdpTestService.exe w Eksplorator plików i kliknij polecenie Uruchom jako administrator.
Spowoduje to wygenerowanie następujących danych wyjściowych.
Testing Udp From Code. Service is started from code... Press <ENTER> to terminate the service and start service from config...
W tej chwili możesz uruchomić klienta, uruchamiając UdpTestClient.exe. Dane wyjściowe generowane przez klienta są następujące.
0 3 6 9 12 Press <ENTER> to complete test.
Dane wyjściowe usługi są następujące.
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
Aplikacja usługi wyświetla komunikat
The client transaction has flowed to the service
, jeśli może być zgodny z identyfikatorem transakcji wysłanym przez klienta, wclientTransactionId
parametrzeCalculatorService.Add()
operacji, do identyfikatora transakcji usługi. Dopasowanie jest uzyskiwane tylko wtedy, gdy transakcja klienta przepływa do usługi.Aby uruchomić aplikację kliencją względem punktów końcowych opublikowanych przy użyciu konfiguracji, naciśnij klawisz ENTER w oknie aplikacji usługi, a następnie ponownie uruchom klienta testowego. W usłudze powinny zostać wyświetlone następujące dane wyjściowe.
Testing Udp From Config. Service is started from config... Press <ENTER> to terminate the service and exit...
Uruchomienie klienta względem usługi powoduje teraz wygenerowanie podobnych danych wyjściowych, jak poprzednio.
Aby ponownie wygenerować kod klienta i konfigurację przy użyciu Svcutil.exe, uruchom aplikację usługi, a następnie uruchom następujące polecenie Svcutil.exe z katalogu głównego przykładu.
svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
Należy pamiętać, że Svcutil.exe nie generuje konfiguracji rozszerzenia powiązania dla elementu
sampleProfileUdpBinding
. Należy dodać ją ręcznie.<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>