WS-Transaktionsfluss
Im TransactionFlow-Beispiel werden die Verwendung einer clientkoordinierten Transaktion und die Client- und Serveroptionen für den Transaktionsfluss unter Verwendung des WS-Atomic Transaction-Protokolls oder des OleTransactions-Protokolls erläutert. Dieses Beispiel basiert auf dem Beispiel Erste Schritte, das einen Rechnerdienst implementiert. Die Vorgänge sollen jedoch die Verwendung von TransactionFlowAttribute
mit der TransactionFlowOption-Enumeration veranschaulichen, die ermittelt, in welchem Umfang der Transaktionsfluss aktiviert ist. Innerhalb des Bereichs des Transaktionsflusses wird ein Protokoll der angeforderten Vorgänge in eine Datenbank geschrieben. Dieses bleibt dort erhalten, bis die clientkoordinierte Transaktion fertiggestellt wurde. Wenn die Clienttransaktion nicht fertiggestellt wird, stellt die Webdiensttransaktion sicher, dass die entsprechenden Aktualisierungen der Datenbank nicht ausgeführt werden.
Hinweis
Die Setupprozedur und die Buildanweisungen für dieses Beispiel befinden sich am Ende dieses Themas.
Nach der Initiierung einer Verbindung zu dem Dienst und einer Transaktion greift der Client auf mehrere Dienstvorgänge zu. Der Vertrag für den Dienst ist folgendermaßen definiert, dabei veranschaulicht jeder der Vorgänge eine andere Einstellung für die TransactionFlowOption
.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
double Add(double n1, double n2);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
double Subtract(double n1, double n2);
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
Auf diese Weise werden die Vorgänge in der Reihenfolge, in der sie verarbeitet werden sollten, definiert:
Eine
Add
-Vorgangsanforderung muss einen Transaktionsfluss beinhalten.Eine
Subtract
-Vorgangsanforderung kann einen Transaktionsfluss beinhalten.Eine
Multiply
-Vorgangsanforderung darf keinen Transaktionsfluss durch die explizite NotAllowed-Einstellung beinhalten.Eine
Divide
-Vorgangsanforderung darf keinen Transaktionsfluss durch die Auslassung einesTransactionFlow
-Attributs beinhalten.
Um den Transaktionsfluss zu aktivieren, müssen zusätzlich zu den entsprechenden Vorgangsattributen Bindungen mit aktivierter <transactionFlow>-Eigenschaft verwendet werden. In diesem Beispiel macht die Dienstkonfiguration zusätzlich zu einem Metadatenaustausch-Endpunkt einen TCP-Endpunkt und einen HTTP-Endpunkt verfügbar. Der TCP-Endpunkt und der HTTP-Endpunkt verwenden die folgenden Bindungen, für die jeweils die <transactionFlow>-Eigenschaft aktiviert ist.
<bindings>
<netTcpBinding>
<binding name="transactionalOleTransactionsTcpBinding"
transactionFlow="true"
transactionProtocol="OleTransactions"/>
</netTcpBinding>
<wsHttpBinding>
<binding name="transactionalWsatHttpBinding"
transactionFlow="true" />
</wsHttpBinding>
</bindings>
Hinweis
Die vom System bereitgestellte netTcpBinding ermöglicht die Festlegung des transactionProtocol, während die vom System bereitgestellte wsHttpBinding nur das interoperablere WSAtomicTransactionOctober2004-Protokoll verwendet. Das OleTransactions-Protokoll ist nur für die Verwendung durch WCF-Clients (Windows Communication Foundation) verfügbar.
Für die Klasse, die die ICalculator
-Schnittstelle implementiert, werden alle Methoden mit auf TransactionScopeRequired festgelegter true
-Eigenschaft attributiert. Diese Einstellung deklariert, dass alle innerhalb der Methode ausgeführten Aktionen innerhalb des Bereichs einer Transaktion auftreten. In diesem Fall beinhalten die ausgeführten Aktionen auch Aufzeichnung in der Protokolldatenbank. Wenn die Vorgangsanforderung einen Transaktionsfluss beinhaltet, treten die Aktionen innerhalb des Bereichs der eingehenden Transaktion auf, oder es wird automatisch ein neuer Transaktionsbereich erstellt.
Hinweis
Die TransactionScopeRequired-Eigenschaft definiert das Verhalten lokal in Bezug auf die Implementierungen der Dienstmethode und definiert nicht die Fähigkeit des Clients für oder die Anforderung eines Transaktionsflusses.
// Service class that implements the service contract.
[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class CalculatorService : ICalculator
{
[OperationBehavior(TransactionScopeRequired = true)]
public double Add(double n1, double n2)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Adding {0} to {1}", n1, n2));
return n1 + n2;
}
[OperationBehavior(TransactionScopeRequired = true)]
public double Subtract(double n1, double n2)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Subtracting {0} from {1}", n2, n1));
return n1 - n2;
}
[OperationBehavior(TransactionScopeRequired = true)]
public double Multiply(double n1, double n2)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Multiplying {0} by {1}", n1, n2));
return n1 * n2;
}
[OperationBehavior(TransactionScopeRequired = true)]
public double Divide(double n1, double n2)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Dividing {0} by {1}", n1, n2));
return n1 / n2;
}
// Logging method omitted for brevity
}
Auf dem Client werden die TransactionFlowOption
-Einstellungen des Diensts für die Vorgänge in der generierten Definition des Clients für die ICalculator
-Schnittstelle wiedergegeben. Außerdem werden die transactionFlow
-Eigenschaftseinstellungen des Diensts in der Anwendungskonfiguration des Clients wiedergegeben. Der Client kann den Transport und das Protokoll auswählen, indem er den entsprechenden endpointConfigurationName
auswählt.
// Create a client using either wsat or oletx endpoint configurations
CalculatorClient client = new CalculatorClient("WSAtomicTransaction_endpoint");
// CalculatorClient client = new CalculatorClient("OleTransactions_endpoint");
Hinweis
Das beobachtete Verhalten dieses Beispiels ist das gleiche, unabhängig davon, welches Protokoll oder welcher Transport ausgewählt wird.
Nach der Initiierung der Verbindung zu dem Dienst erstellt der Client einen neuen TransactionScope
um die Aufrufe der Dienstvorgänge.
// Start a transaction scope
using (TransactionScope tx =
new TransactionScope(TransactionScopeOption.RequiresNew))
{
Console.WriteLine("Starting transaction");
// Call the Add service operation
// - generatedClient will flow the required active transaction
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine(" Add({0},{1}) = {2}", value1, value2, result);
// Call the Subtract service operation
// - generatedClient will flow the allowed active transaction
value1 = 145.00D;
value2 = 76.54D;
result = client.Subtract(value1, value2);
Console.WriteLine(" Subtract({0},{1}) = {2}", value1, value2, result);
// Start a transaction scope that suppresses the current transaction
using (TransactionScope txSuppress =
new TransactionScope(TransactionScopeOption.Suppress))
{
// Call the Subtract service operation
// - the active transaction is suppressed from the generatedClient
// and no transaction will flow
value1 = 21.05D;
value2 = 42.16D;
result = client.Subtract(value1, value2);
Console.WriteLine(" Subtract({0},{1}) = {2}", value1, value2, result);
// Complete the suppressed scope
txSuppress.Complete();
}
// Call the Multiply service operation
// - generatedClient will not flow the active transaction
value1 = 9.00D;
value2 = 81.25D;
result = client.Multiply(value1, value2);
Console.WriteLine(" Multiply({0},{1}) = {2}", value1, value2, result);
// Call the Divide service operation.
// - generatedClient will not flow the active transaction
value1 = 22.00D;
value2 = 7.00D;
result = client.Divide(value1, value2);
Console.WriteLine(" Divide({0},{1}) = {2}", value1, value2, result);
// Complete the transaction scope
Console.WriteLine(" Completing transaction");
tx.Complete();
}
Console.WriteLine("Transaction committed");
Die Aufrufe der Vorgänge sehen folgendermaßen aus:
Die
Add
-Anforderung übergibt die erforderliche Transaktion an den Dienst, und die Aktionen des Diensts treten innerhalb des Bereichs der Transaktion des Clients auf.Die erste
Subtract
-Anforderung übergibt die zulässige Transaktion ebenfalls an den Dienst, und die Aktionen des Diensts treten erneut innerhalb des Bereichs der Transaktion des Clients auf.Die zweite
Subtract
-Anforderung wird innerhalb eines neuen Transaktionsbereichs ausgeführt, der mit derTransactionScopeOption.Suppress
-Option deklariert wird. Auf diese Weise wird die initiale äußere Transaktion des Clients unterdrückt, und die Anforderung übergibt keine Transaktion an den Dienst. Mithilfe dieses Verfahrens kann ein Client explizit deaktiviert werden, und es wird verhindert, dass eine Transaktion an einen Dienst übergeben wird, wenn diese nicht erforderlich ist. Die Aktionen des Diensts treten innerhalb des Bereichs einer neuen und unverbundenen Transaktion auf.Die
Multiply
-Anforderung übergibt keine Transaktion an den Dienst, da die generierte Definition derICalculator
-Schnittstelle ein TransactionFlowAttribute beinhaltet, für das TransactionFlowOptionNotAllowed
festgelegt wurde.Die
Divide
-Anforderung übergibt keine Transaktion an den Dienst, da die generierte Definition derICalculator
-Schnittstelle wiederum keinTransactionFlowAttribute
beinhaltet. Die Aktionen des Diensts treten erneut innerhalb des Bereichs einer anderen neuen und unverbundenen Transaktion auf.
Wenn Sie das Beispiel ausführen, werden die Anforderungen und Antworten für den Vorgang im Clientkonsolenfenster angezeigt. Drücken Sie im Clientfenster die EINGABETASTE, um den Client zu schließen.
Starting transaction
Add(100,15.99) = 115.99
Subtract(145,76.54) = 68.46
Subtract(21.05,42.16) = -21.11
Multiply(9,81.25) = 731.25
Divide(22,7) = 3.14285714285714
Completing transaction
Transaction committed
Press <ENTER> to terminate client.
Die Protokollierung der Dienstvorgangsanforderungen wird im Konsolenfenster des Diensts angezeigt. Drücken Sie im Clientfenster die EINGABETASTE, um den Client zu schließen.
Press <ENTER> to terminate the service.
Writing row to database: Adding 100 to 15.99
Writing row to database: Subtracting 76.54 from 145
Writing row to database: Subtracting 42.16 from 21.05
Writing row to database: Multiplying 9 by 81.25
Writing row to database: Dividing 22 by 7
Nach einer erfolgreichen Ausführung wird der Transaktionsbereich des Clients abgeschlossen, und alle Aktionen innerhalb des Bereichs werden ausgeführt. Vor allem werden die erwähnten fünf Datensätze in der Datenbank des Diensts beibehalten. Die beiden ersten sind innerhalb des Bereichs der Transaktion des Clients aufgetreten.
Wenn im TransactionScope
des Clients eine Ausnahme aufgetreten ist, kann die Transaktion nicht abgeschlossen werden. Dies führt dazu, dass die innerhalb des Bereichs protokollierten Datensätze nicht an die Datenbank übergeben werden. Dieser Effekt kann beobachtet werden, wenn das Beispiel erneut ausgeführt wird, nachdem der Aufruf zum Abschließen des äußeren TransactionScope
auskommentiert wurde. Bei einer solchen Ausführung werden nur die letzen drei Aktionen protokolliert (die zweite Subtract
-, die Multiply
- und die Divide
-Anforderung), da die Clienttransaktion nicht an diese übergeben wurde.
So können Sie das Beispiel einrichten, erstellen und ausführen
Um die C#- oder Visual Basic .NET-Version der Projektmappe zu erstellen, befolgen Sie die Anweisungen unter Erstellen der Windows Communication Foundation-Beispiele.
Vergewissern Sie sich, dass SQL Server Express Edition oder SQL Server installiert ist. Stellen Sie außerdem sicher, dass die Verbindungszeichenfolge in der Anwendungskonfigurationsdatei des Diensts ordnungsgemäß festgelegt ist. Wenn Sie das Beispiel ohne eine Datenbank ausführen möchten, legen Sie den
usingSql
-Wert in der Anwendungskonfigurationsdatei des Diensts auffalse
fest.Wenn Sie das Beispiel in einer Konfiguration mit einem Computer oder über Computer hinweg ausführen möchten, folgen Sie den Anweisungen unter Durchführen der Windows Communication Foundation-Beispiele.
Hinweis
Aktivieren Sie bei einer computerübergreifenden Konfiguration Distributed Transaction Coordinator entsprechend den nachfolgenden Anweisungen, und aktivieren Sie mit dem Tool WsatConfig.exe aus dem Windows SDK die Netzwerkunterstützung von WCF-Transaktionen. Weitere Informationen zum Einrichten von „WsatConfig.exe“ finden Sie unter Konfigurieren der WS-Atomic Transaction-Unterstützung.
Wenn Sie das Beispiel auf demselben Computer oder auf anderen Computern ausführen, müssen Sie Microsoft Distributed Transaction Coordinator (MSDTC) konfigurieren, um den Transaktionsfluss über das Netzwerk zu ermöglichen, und mit dem Tool „WsatConfig.exe“ die Netzwerkunterstützung für WCF-Transaktionen aktivieren.
So konfigurieren Sie Microsoft Distributed Transaction Coordinator (MSDTC) zum Ausführen des Beispiels
Konfigurieren Sie MSDTC auf einem Dienstcomputer unter Windows Server 2003 oder Windows XP entsprechend den folgenden Anweisungen so, dass eingehende Netzwerktransaktionen zugelassen werden.
Navigieren Sie im Menü Start zu Systemsteuerung, Verwaltung und dann zu Komponentendienste.
Erweitern Sie Komponentendienste. Öffnen Sie den Ordner Computer.
Klicken Sie mit der rechten Maustaste auf Arbeitsplatz, und wählen Sie Eigenschaften aus.
Klicken Sie auf der Registerkarte MSDTC auf Sicherheitskonfiguration.
Aktivieren Sie DTC-Netzwerkzugriff und Eingehende zulassen.
Klicken Sie auf OK und dann auf Ja, um den MSDTC-Dienst neu zu starten.
Klicken Sie auf OK , um das Dialogfeld zu schließen.
Konfigurieren Sie MSDTC auf einem Dienstcomputer unter Windows Server 2008 oder Windows Vista entsprechend den folgenden Anweisungen so, dass eingehende Netzwerktransaktionen zugelassen werden.
Navigieren Sie im Menü Start zu Systemsteuerung, Verwaltung und dann zu Komponentendienste.
Erweitern Sie Komponentendienste. Öffnen Sie den Ordner Computer. Wählen Sie Distributed Transaction Coordinator aus.
Klicken Sie mit der rechten Maustaste auf Distributed Transaction Coordinator, und wählen Sie Eigenschaften aus.
Aktivieren Sie auf der Registerkarte Sicherheit die Optionen DTC-Netzwerkzugriff und Eingehende zulassen.
Klicken Sie auf OK und dann auf Ja, um den MSDTC-Dienst neu zu starten.
Klicken Sie auf OK , um das Dialogfeld zu schließen.
Konfigurieren Sie auf dem Clientcomputer MSDTC zum Zulassen von ausgehenden Netzwerktransaktionen:
Navigieren Sie im Menü Start zu
Control Panel
und dann zu Verwaltung und Komponentendienste.Klicken Sie mit der rechten Maustaste auf Arbeitsplatz, und wählen Sie Eigenschaften aus.
Klicken Sie auf der Registerkarte MSDTC auf Sicherheitskonfiguration.
Aktivieren Sie DTC-Netzwerkzugriff und Ausgehende zulassen.
Klicken Sie auf OK und dann auf Ja, um den MSDTC-Dienst neu zu starten.
Klicken Sie auf OK , um das Dialogfeld zu schließen.