Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Przykład TransactionMessagePropertyUDPTransport jest oparty na przykładzie Transport: UDP w Windows Communication Foundation (WCF) Rozszerzalność transportu. Rozszerza próbkę transportu UDP w celu obsługi niestandardowego przepływu transakcji i demonstruje użycie właściwości TransactionMessageProperty.
Zmiany kodu w przykładzie transportu UDP
Aby zademonstrować przepływ transakcji, przykład zmienia kontrakt usługi dla ICalculatorContract
, aby wymagał zakresu transakcji dla CalculatorService.Add()
. Przykład dodatkowo dodaje do kontraktu operacji System.Guid
ekstra parametr Add
. 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 pomocnicza zawierająca nową funkcjonalność łączenia tokenu propagacji dla bieżącej transakcji z jednostką komunikatu i umieszczania jej w buforze.
W przypadku niestandardowego transportu przepływu transakcji implementacja klienta musi wiedzieć, które operacje usług wymagają przepływu transakcji oraz przekazać te informacje do platformy 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 systemu do komunikatu przy użyciu
TransactionFlowProperty
, jeżeli przepływ transakcji jest wymagany (dzieje się to w operacjiBeforeSendRequest()
).
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 struktury 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)
{
}
}
Kiedy poprzedni mechanizm jest już wdrożony, kod użytkownika tworzy TransactionScope
przed wywołaniem operacji usługi. Inspektor komunikatów zapewnia, że transakcja jest przesyłana do transportu, jeśli jest to wymagane do przesyłania do operacji serwisowej.
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 wpłynęła, zostanie ona dołączona do komunikatu w TransactionMessageProperty
.
message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);
if (transaction != null)
{
TransactionMessageProperty.Set(transaction, message);
}
Gwarantuje to, że dyspozytor pobiera transakcję w czasie przekazywania i wykorzystuje ją, gdy wywołuje operację usługi, do której odnosi się komunikat.
Aby skonfigurować, skompilować i uruchomić przykładowy program
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 Eksploratorze 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 serwisowa wyświetla komunikat
The client transaction has flowed to the service
, jeśli może dopasować identyfikator transakcji wysłany przez klienta w parametrzeclientTransactionId
operacjiCalculatorService.Add()
do identyfikatora transakcji serwisu. Dopasowanie jest możliwe tylko wtedy, gdy transakcja klienta została przekazana do usługi.Aby uruchomić aplikację kliencką względem punktów końcowych opublikowanych poprzez konfigurację, naciśnij 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...
Teraz uruchomienie klienta usługi powoduje wygenerowanie podobnych danych wyjściowych, jak poprzednio.
Aby ponownie wygenerować klientski kod i konfigurację przy użyciu Svcutil.exe, uruchom aplikację usługi, a następnie uruchom następujące polecenie Svcutil.exe z głównego katalogu przykładowego projektu.
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
; trzeba ją dodać 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>