MSDN Magazin > Home > Ausgaben > 2007 > June >  Cutting Edge: Transaktionsworkflows
Cutting Edge
Transaktionsworkflows
Dino Esposito

Codedownload verfügbar unter: CuttingEdge2007_06.exe (182 KB)
Browse the Code Online
Windows® Workflow Foundation, ein Pfeiler von Microsoft® .NET Framework 3.0, stellt das Programmiermodell, das Laufzeitmodul und Tools zum Entwickeln von Workflows bereit, die in .NET-basierte Anwendungen eingebunden oder als Dienste für alle betroffenen Clients verfügbar gemacht werden können. Wann benötigen Sie tatsächlich einen Workflow in einer benutzerdefinierten Lösung?
Ein Workflow hilft sicherlich, wenn die Geschäftslogik und die Regeln häufig Änderungen unterworfen sind. Dies kann vorkommen, weil Sie die gleiche Anwendung an die Anforderungen verschiedener Kunden anpassen müssen oder einfach nur deshalb, weil der Kunde diese Art von Flexibilität speziell verlangt. Ein Workflow kann hinter einem BPM (Business Process Management)-Server liegen, um Unternehmensdienste zu orchestrieren, die den Modulen einer verteilten, zusammengesetzten Anwendung zur Verfügung stehen. Schließlich dient ein Workflow dazu, die Implementierung jeder langfristigen unternehmensübergreifenden Geschäftslogik zu unterstützen.
Aus der Sicht des Entwicklers ist ein Workflow eine Gruppe von Aktivitäten, die gemeinsam das gewünschte Verhalten zum Ausdruck bringen. Wenn ein Workflow dazu dient, eine realistische Geschäftslogik zu modellieren, hat der Workflow unvermeidlich mit der Abwicklung von Transaktionsaufgaben zu tun. Wie würden Sie daher Transaktionssemantik in Windows Workflow Foundation programmieren?
Fast alle Unternehmenssysteme enthalten einen Workflow, der sich um besonders komplexe Probleme kümmert, und fast alle komplexen Probleme enthalten eine Transaktion. Es gibt zwei Haupttypen von Transaktionen, die bewältigt werden müssen. Den ersten Typ bilden klassische, kurzlebige Transaktionen, die oft als ACID-Transaktionen bezeichnet werden. Der andere Typ kann als globale, unternehmensweite Transaktionen mit langer Laufzeit beschrieben werden. Diese Art von Transaktion ist oft eine Komponente eines Geschäftsprozesses und kann aus mehreren ACID-Transaktionen bestehen. Der Erfolg oder Misserfolg dieser ACID-Transaktionen trägt zum Gesamtergebnis des größeren Geschäftsprozesses bei, der durch den Workflow repräsentiert wird.
Windows Workflow Foundation stellt Ad-hoc-Aktivitäten bereit, damit die Implementierung von ACID-Transaktionen und von unternehmensweiten Transaktionen nicht nur effektiv ist, sondern sich auch verhältnismäßig leicht programmieren und aktualisieren lässt. In diesem Artikel wird die Gruppe der Aktivitäten erörtert, die alle Arten von Transaktionsaufgaben bereitstellen, die in einen Windows Workflow Foundation-Workflow integriert werden könnten.

Kategorisieren von Transaktionsaufgaben
Ein Windows Workflow Foundation-Workflow kann einen oder mehrere Blöcke von Aktivitäten enthalten, die als atomare Aufgaben betrachtet werden sollten. Wenn Sie derartige Aktivitäten in einem äußersten Transaktionsblock wrappen, wird durch die Windows Workflow Foundation-Laufzeit sichergestellt, dass alle enthaltenen Aktivitäten entweder gelingen oder fehlschlagen. Solche Transaktionen werden als ACID-Transaktionen bezeichnet, womit auf die Beschreibung des erwarteten Verhaltens (Atomic, Consistent, Isolated and Durable – atomar, konsistent, isoliert und dauerhaft) verwiesen wird (siehe Abbildung 1).

Feature Beschreibung
Atomar Entweder alle Operationen der Transaktion werden erfolgreich abgeschlossen, oder keine von ihnen wird abgeschlossen.
Konsistent Wenn die Transaktion beginnt und endet, müssen sich die an der Transaktion beteiligten Ressourcen in einem zulässigen Zustand befinden. Die Transaktion darf nicht gegen Integritätseinschränkungen oder Geschäftsregeln verstoßen.
Isoliert In der Transaktion enthaltene Operationen sind von allen anderen Operationen isoliert. Keine Operation außerhalb der Transaktion kann die Daten in einem zwischengelagerten Zustand sehen.
Dauerhaft Sobald die Operation erfolgreich abgeschlossen wurde, sind ihre Auswirkungen permanent und können nicht mehr rückgängig gemacht werden.
Ein ganzer Windows Workflow Foundation-Workflow kann auch als eine einzelne Transaktionen mit langer Laufzeit (long-running transaction, LRT) behandelt werden. In diesem Fall verwenden Sie die Workflowsemantik, um einen Geschäftsprozess oder einen Teil eines Geschäftsprozesses zu beschreiben, an dem mehrere Dienste, unterschiedliche Softwareplattformen und verschiedene Unternehmen beteiligt sind. Eine LRT-Transaktion kann Minuten, Tage oder sogar Wochen in Anspruch nehmen, bevor das Ergebnis bekannt wird. Der Prozess kann eine aus mehreren Schritten bestehende Operation implementieren und sich über die Informationssysteme mehrerer Unternehmen erstrecken. In der Regel besteht eine LRT-Transaktion aus einer oder mehreren ACID-Transaktionen, die unabhängig voneinander gelingen oder fehlschlagen. Da jedoch der Prozess lang und komplex ist, kann es vorkommen, dass ein weiterer Schritt im Prozess eine Bedingung bestimmt, die mit dem Ergebnis einer vorherigen ACID-Transaktion nicht vereinbar ist. In dieser Situation ist es dann zu spät, die Ergebnisse einer bereits übermittelten Transaktion durch einen Rollback rückgängig zu machen, aber ihre Auswirkungen müssen auf irgendeine Weise kompensiert werden.
Obwohl das Wort „Transaktion“ sowohl für ACID-Transaktionen als auch für Prozesse mit langer Laufzeit verwendet wird, gibt es zwischen beiden Transaktionsarten einen deutlichen Unterschied. Das Wort „Transaktion“ bezieht sich in der Regel auf eine Abfolge von Operationen, die als eine einzige Arbeitseinheit verarbeitet und behandelt werden müssen. Die Operation ist normalerweise nach einigen Sekunden abgeschlossen und umfasst nie ein Anhalten oder einen unbestimmten Zeitraum, in dem auf eine Benutzereingabe oder einen anderen Eingriff durch einen Menschen gewartet wird. Sie erfordert keine Dauerhaftigkeit und wird als Alles-oder-Nichts-Operation angesehen. Die Beschreibung einer typischen Transaktion ist im Wesentlichen mit der Beschreibung einer ACID-Transaktion identisch.
Was also ist eine Transaktion mit langer Laufzeit eigentlich genau? Ist sie im Grunde eine ACID-Transaktion, die länger dauert und bei Bedarf angehalten und später wieder fortgesetzt werden kann? Bei einer Transaktion mit langer Laufzeit wird der Begriff „Transaktion“ viel großzügiger verwendet. Eine LRT-Transaktion ist ein zusammenhängender Satz von Aktionen, bei denen lose angekoppelte und unabhängige Systeme eine Rolle spielen.
Die lange Laufzeit bestimmter Transaktionen kann die Aufgabe, sie als ACID-Transaktion zu verwalten, viel komplizierter oder sogar praktisch unmöglich machen. ACID-Transaktionen erfordern oft, dass einige wichtige Daten während der Laufzeit der Transaktion gesperrt werden, was bei Transaktionen, die nur wenige Sekunden dauern, nicht ins Gewicht fällt. Wenn jedoch eine der an einer speziellen Unternehmenstransaktion beteiligten Ressourcen nicht während der gesamten Dauer des Prozesses gesperrt bleiben kann, ist das ACID-Muster nicht geeignet. Die verschiedenen Operationen müssen als unabhängige Operationen abgeschlossen werden, wobei eine gewisse umfassende Logik zum Tragen kommt, von der Aktionen und Ergebnisse orchestriert werden und die in der Lage sein muss, ggf. Irrtümer und Fehler auszugleichen. Die Orchestrierungslogik kann in einem Windows Workflow Foundation-Workflow integriert werden, der als BPM-Server dient, oder in einer zusammengesetzten Windows Workflow Foundation-Aktivität erstellt werden, die anschließend als äußerster Workflow verwendet wird.

ACID-Transaktionen in Workflows
Eine ACID-Transaktion ist eine kurzlebige Transaktion, bei der die Entscheidung, ob sie übermittelt oder durch einen Rollback rückgängig gemacht wird, in der Regel innerhalb weniger Sekunden getroffen wird. Die Rollbacklogik muss in der Lage sein, die laufende Operation und alle Zwischenschritte, aus denen sie möglicherweise besteht, abzubrechen und die Auswirkungen vorheriger Operationen auszugleichen. Aus der Sicht des Benutzers wirkt dies so, als wäre nichts geschehen. Eine in Form einer gespeicherten Prozedur programmierte typische Datenbanktransaktion, die atomar an zwei oder mehreren Tabellen ausgeführt wird, ist ein ideales Beispiel für eine ACID-Transaktion.
Eine ACID-Transaktion kann lokal oder verteilt sein. Eine lokale Transaktion betrifft eine einzelne Ressource, z. B. die Datenbank, mit der Sie verbunden sind. Eine verteilte Transaktion erstreckt sich über mehrere heterogene Ressourcen und erfordert einen Transaktionsverarbeitungsmonitor. Der Distributed Transaction Coordinator (DTC) ist der Transaktionsverarbeitungsmonitor für Microsoft Windows 2000 und neuere Windows-Versionen.
Um in einem Workflow ein Transaktionssegment zu erstellen, verwenden Sie die TransactionScope-Aktivität (siehe Abbildung 2). Alle Aktivitäten, die im Bereich der Transaktion zusammengesetzt werden, bilden eine Arbeitseinheit, die dem klassischen ACID-Schema entspricht. Wenn alle untergeordneten Aktivitäten erfolgreich abgeschlossen wurden, wird die Transaktion übermittelt und der Workflow fortgesetzt. Wenn von einer der untergeordneten Aufgaben eine Ausnahme ausgelöst wird, führt die TransactionScope-Aktivität eine Rollbackoperation durch.
Abbildung 2 TransactionScope-Aktivität in Visual Studio 2005 (Klicken Sie zum Vergrößern auf das Bild)
Der in Abbildung 2 dargestellte Beispielworkflow nimmt eine Bestellnummer entgegen und fährt mit dem Zahlungsvorgang fort. Zuerst wird Geld vom Konto des Kunden abgebucht, und danach wird dieses Geld auf dem Konto des Anbieters eingezahlt. Diese Operation muss insofern atomar sein, als entweder beide Schritte erfolgreich abgeschlossen werden oder beide Schritte fehlschlagen. In jedem dieser Fälle müssen die beteiligten Ressourcen am Ende einen konsistenten Zustand haben, in dem keine Einschränkung verletzt wird und die Datenintegrität gewährleistet ist.
Die internen Schritte sind sowohl für den Entwickler als auch für den Endbenutzer transparent. Im Idealfall sollten keine externen Softwarekomponenten auf den Zustand von Transaktionsressourcen zugreifen, bevor der endgültige Zustand erreicht wurde (diese Einschränkung wird manchmal zugunsten der Leistung und evtl. auf Kosten der Richtigkeit vernachlässigt). Isolierung erfordert daher eine serialisierbare zugrunde liegende Transaktion und somit die kostspieligste Art von Transaktion, die von Datenbanken und ähnlichen Ressourcenmanagern unterstützt wird.

Untersuchen der TransactionScope-Aktivität
In Windows Workflow Foundation verwenden Sie die TransactionScope-Aktivität als Container für untergeordnete Aktivitäten, die sequenziell und unter voller Berücksichtigung der ACID-Semantik ausgeführt werden. Wenn Sie die Parallel-Aktivität innerhalb eines Transaktionsbereichs verwenden, dann werden Aktivitäten innerhalb der Aktivität gleichzeitig, aber in Bezug auf den restlichen Bereich sequenziell ausgeführt.
Die TransactionScope-Aktivität macht zwei Eigenschaften verfügbar, die deklarativ verwendet werden können, um die Transaktion zu konfigurieren: IsolationLevel und TimeoutDuration. Die „IsolationLevel-Eigenschaft hat den Typ „IsolationLevel“, eine im Namespace „System.Transactions“ definierte Aufzählung. Die Isolierungsebene einer Transaktion bestimmt, welche Ebene des Zugriffs Transaktionen auf interne Daten haben, bevor eine Transaktion abgeschlossen wird. Abbildung 3 zeigt im Detail, was mit den am meisten verwendeten Ebenen geschieht.

Ebene Beschreibung
READ UNCOMMITTED Dies ist die am wenigsten einschränkende Ebene, die von anderen Transaktionen aktivierte Sperren ignoriert. Sie können geänderte Daten lesen, die noch nicht von anderen Transaktionen übermittelt wurden (unsauberes Lesen).
READ COMMITTED Dies ist die Standardisolierungsebene für SQL Server, die unsauberes Lesen verhindert. Sie ermöglicht jedoch für Transaktionen, Daten innerhalb der aktuellen Transaktion zu ändern, einzufügen oder zu löschen. Dies kann u. U. nichtwiederholbare Lesevorgänge und Phantomzeilen erzeugen.
REPEATABLE READ Dies verhindert unsauberes Lesen und hindert andere Transaktionen daran, Daten zu ändern oder zu löschen, die von der aktuellen Transaktion eingelesen wurden. Neue Daten können eingefügt werden.
SERIALIZABLE Dies ist die am stärksten einschränkende Ebene, die alle Sperren beibehält, bis die Transaktion abgeschlossen ist. Solange die Transaktion nicht abgeschlossen ist, sind keine Lesevorgänge oder Aktualisierungen möglich.
SNAPSHOT Diese nur für SQL Server 2005 verfügbare Ebene legt fest, dass Daten, die innerhalb einer Transaktion gelesen werden, niemals Änderungen wiedergeben, die von anderen gleichzeitigen Transaktionen vorgenommen werden.
CHAOS In diesem Fall können die ausstehenden Änderungen von stärker isolierten Transaktionen nicht überschrieben werden.
Der Standardwert für IsolationLevel ist „Serializable“ (Serialisierbar). Dies ist die sicherste und kostspieligste Option, die nicht unbedingt in allen Fällen erforderlich ist. Eine serialisierbare Transaktion sperrt Daten für die Dauer der Transaktion, um zu verhindern, dass andere gleichzeitig ausgeführte Transaktionen auf dieselben Daten zugreifen. Die TransactionScope-Aktivität schränkt diese Option jedoch ein, indem sie ein Timeout von 30 Sekunden festlegt. Wenn Sie sich Sorgen über Gleichzeitigkeit machen und unter SQL Server™ 2005 Transaktionen ausführen müssen, können Sie die neue Isolierungsebene „Snapshot“ (Momentaufnahme) auswählen. Im Gegensatz zu einigen der anderen Isolierungsebenen ist „Snapshot“ nicht darauf beschränkt, nur über ein Programm eingestellt werden zu können. Es kann auf SQL Server 2005-Datenbanken angewendet und mithilfe der folgenden Anweisung administrativ konfiguriert werden:
ALTER DATABASE <name> SET ALLOW_SNAPSHOT_ISOLATION ON
Die TransactionScope-Aktivität ist ein um eine Instanz der TransactionScope-Klasse von .NET Framework 2.0 (eine der programmiererfreundlichsten Klassen, die je entwickelt wurden) herum aufgebauter Wrapper. Wrappen Sie einfach alles in ein TransactionScope-Objekt, und Sie sind schon so gut wie fertig. Das Objekt kümmert sich um alles Weitere. Es bestimmt, ob Sie eine lokale oder eine verteilte Transaktion benötigen, trägt alle erforderlichen verteilten Ressourcen ein und fährt ansonsten mit der lokalen Verarbeitung fort. Wenn der Code einen Punkt erreicht, an dem er lokal nicht ausgeführt werden kann, wird er nötigenfalls zum DTC weitergeleitet.
Sie können alle Objekte mit einer Transaktion eintragen, die die ITransaction-Schnittstelle implementiert. Die Liste umfasst alle ADO.NET 2.0-Datenanbieter sowie MSMQ (Microsoft Message Queue).
Wenn ein Code die Complete-Methode des TransactionScope-Objekts aufruft, zeigt dies an, dass alle Operationen im Bereich der Transaktion erfolgreich abgeschlossen wurden. Beachten Sie, dass die Methode eine verteilte Transaktion nicht physisch beendet, da die Übermittlungsoperation nach wie vor von TransactionScope gestartet wird. Allerdings können Sie die verteilte Transaktion nicht weiter verwenden, sobald Sie die Complete-Methode aufgerufen haben.
Die TransactionScope-Aktivität macht Transaktionsaufgaben so einfach wie das Gruppieren von Aktivitäten im Visual Studio® 2005-Designer. Sehen Sie sich den Code an, der hinter den in Abbildung 2 gezeigten Aktivitäten steht. Der Schlüsselcode ist in Abbildung 4 zu sehen.
private void WithdrawFunds_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine(“Funds withdrawn to process order: #” + 
        orderNo.ToString());

    // Execute a SQL operation on ClientAccount
    SqlHelper.ExecuteNonQuery(conn,
        System.Data.CommandType.Text,
        “use bank update ClientAccount set Balance = Balance - 100”);
}

private void AddFunds_ToVendor_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine(“Funds added to the vendor’s account for order #” + 
        orderNo.ToString());

    // Execute a SQL operation on VendorAccount
    SqlHelper.ExecuteNonQuery(conn,
        System.Data.CommandType.Text,
        “use bank update VendorAccount set Balance = Balance + 100”);
}
Der Transaktionscode enthält drei Code-Aktivitäten. Die erste führt einen Datenbankbefehl aus und aktualisiert den Saldo des Kundenkontos, indem sie einen bestimmten Betrag subtrahiert. Danach wird derselbe Betrag zum Saldo des Anbieterkontos addiert. Aufgrund der Eigenschaften des zugrunde liegenden TransactionScope-Objekts können Datenbanken leicht verteilt werden.
Noch wichtiger ist jedoch, dass die Übermittlung der Beträge eine atomare Operation sein muss. Wenn der Ausführungsablauf das Ende der TransactionScope-Aktivität erreicht, wird die Transaktion erfolgreich abgeschlossen und übermittelt. Soll an irgendeinem Punkt eine Ausnahme ausgelöst werden, werden von der Transaktion automatisch durch Rollback alle Arbeitsgänge rückgängig gemacht. Alle diese Vorgänge sind für den Workflowentwickler vollkommen transparent. Als Workflowentwickler brauchen Sie sich nicht um Kompensierung und noch nicht einmal um Fehlerbehebung zu kümmern.
Der Beispielcode löst eine Ausnahme aus, wenn die Bestellnummer ungerade ist, wie der Code hinter der letzten Aktivität in Abbildung 2 zeigt:
void CheckConsistency_ExecuteCode(object sender, EventArgs e)
{
    if (orderNo % 2 > 0)
        throw new DiscontinuedProductException();
}
Die ausgelöste Ausnahme ist ein benutzerdefiniertes Ausnahmeobjekt, das eine bestimmte Situation in der Anwendung beschreibt. Dies genügt, um den Rollbackmechanismus des zugrunde liegenden .NET-Objekts TransactionScope auszulösen. Abbildung 5 zeigt die Anwendung in Aktion und die Meldung, die Sie im Fall eines Rollbacks erhalten. Abbildung 6 zeigt eine erfolgreiche Transaktion, die übermittelt wurde.
Abbildung 6 Überweisung wird übermittelt (Klicken Sie zum Vergrößern auf das Bild)
Abbildung 5 Überweisung wird abgebrochen (Klicken Sie zum Vergrößern auf das Bild)
Wenn Sie eine Ausnahme abfangen müssen, die im Umfeld des Workflows ausgelöst wird, insbesondere Ausnahmen im Zusammenhang mit der TransactionScope-Aktivität, dann wechseln Sie in die Fehlerhandleransicht, und fügen Sie eine FaultHandler-Aktivität hinzu. Danach können Sie die Aktivität so konfigurieren, dass sie einen bestimmten Ausnahmetyp abfängt und seinem Bereich so viele Aktivitäten hinzufügt, wie zur Verarbeitung der Ausnahme erforderlich sind. Dies ist in einem ACID-Szenario nicht besonders sinnvoll, aber Sie könnten normalerweise eine andere Transaktion ausführen, während die Ausnahme verarbeitet wird. Der Zweck des Fehlerhandlers besteht darin, die partielle und erfolglose Arbeit einer Aktivität, in der eine Ausnahme aufgetreten ist, rückgängig zu machen. Wenn ein Transaktionsbereich betroffen ist, erfolgt der Rollback allerdings automatisch, und es brauchen keine Ad-hoc-SQL-Befehle geschrieben werden, um die Auswirkungen vorheriger Datenbankoperationen rückgängig zu machen.

Konfigurieren eines Transaktionsworkflows für die Ausführung
Für die Verwendung der TransactionScope-Aktivität gelten einige Einschränkungen, die Sie beachten müssen. Erstens können Sie den Workflow nicht aus einer Transaktion heraus mithilfe der Suspend-Aktivität anhalten. Zweitens kann eine TransactionScope-Aktivität nicht innerhalb einer anderen TransactionScope-Aktivität oder in anderen Aktivitäten, die die ICompensatableActivity-Schnittstelle implementieren, verschachtelt werden. Beispiele für solche Aktivitäten sind die Aktivitäten „CompensatableTransactionScope“ und „CompensatableSequence“ in der Toolbox.
Ein Transaktionsworkflow erfordert einen Persistenzdienst. Wenn kein Persistenzdienst verfügbar ist, wird eine Ausnahme ausgelöst. Sie registrieren einen Persistenzdienst wie folgt mit der Workflowlaufzeit in der Clientanwendung:
string conn = “...”;
workflowRuntime.AddService(
    new SqlWorkflowPersistenceService(conn));
Die SqlWorkflowPersistenceService-Klasse ist der standardmäßige Persistenzdienst und basiert auf einer SQL Server-Datenbank. Dauerhaftigkeit tritt dann auf, wenn eine ACID-Transaktion abgeschlossen wird oder wenn die Workflowinstanz untätig oder per Programm entladen wird. Das Workflowlaufzeitmodul ruft Methoden für den Persistenzdienst auf, um den Zustand der Workflowinstanz zu speichern. Das Workflowlaufzeitmodul bestimmt, wann und wie Dauerhaftigkeit durchgeführt wird. Der Dienst sorgt für das eigentliche Speichern und Laden des Workflowstatus im und aus dem Datenspeicher Ihrer Wahl.
Die von der SqlWorkflowPersistenceService-Klasse verwendete Datenbank wird nicht bei der Installation erstellt, aber es werden zur späteren Verwendung Skripts auf den Clientcomputer kopiert. Der Pfad des Skripts lautet:
%WINDOWS%\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL
Sie können den Standardnamen der Datenbank ändern. Die Struktur der untergeordneten Tabellen kann jedoch nicht geändert werden. Um ein anderes Datenbanklayout zu verwenden, benötigen Sie einen benutzerdefinierten Persistenzdienst. Im Grunde ist ein benutzerdefinierter Persistenzdienst eine von WorkflowPersistenceService erbende Klasse.

Modellieren von Geschäftsprozessen mit Windows Workflow Foundation
Eine ACID-Transaktion ist nur ein kleiner Teil eines realistischen Workflows. Mehrere ACID-Transaktionen werden in der Regel in einem einzigen Workflow kombiniert, um einen bestimmten Geschäftsprozess auszudrücken. So kann beispielsweise eine Transaktionsaktivität erfolgreich abgeschlossen werden, aber in einer anderen Aktivität, die im Workflow an späterer Stelle ausgeführt wird, eine Ausnahme ausgelöst werden. In diesem Fall ist kein automatischer Rollback möglich. Der Rollback ist im Kontext einer Transaktion möglich, da diese von einem Ressourcenmanager überwacht wird, der Operationen verfolgt und sie nötigenfalls abbricht. Workflows dagegen unterstützen die Transaktionssemantik nicht in vollem Umfang, sondern unterstützen lediglich Transaktionsaktivitäten.
Wenn Sie Windows Workflow Foundation zum Modellieren eines Geschäftsprozesses verwenden, sind möglicherweise verschiedene Transaktionsblöcke über den gesamten Workflow verstreut. Windows Workflow Foundation stellt zwei Arten von Transaktionsaktivitäten zur Verfügung: ACID-Transaktionen und kompensierbare Transaktionen. Ein Beispiel für eine ACID-Transaktion ist die TransactionScope-Aktivität. ACID-Transaktionen werden entweder übermittelt oder durch Rollback rückgängig gemacht, und ihre Ergebnisse werden dauerhaft gespeichert. Doch was können Sie tun, wenn Sie Transaktionsaufgaben im Kontext eines größeren Prozesses implementieren müssen, in dem Sie die Daten nicht lange sperren können und in der Lage sein müssen, vorherige Übermittlungen durch Rollback rückgängig zu machen? Dazu benötigen Sie eine besondere Art von Transaktionsaktivität: die CompensatableTransactionScope-Aktivität.
CompensatableTransactionScope unterstützt einen Kompensierungsmechanismus. Eine Kompensierung besteht aus einer Logik, die Sie an einem bestimmten Punkt ausführen, um die Auswirkungen vorheriger Operationen rückgängig zu machen, zu reduzieren oder zu kompensieren. Der springende Punkt liegt darin, dass die kompensierbare Transaktion untergeordnete ACID-Transaktionen enthalten könnte, die nach ihrer Übermittlung nicht mehr durch Rollback rückgängig gemacht werden können. Im Falle weiterer Fehler müssen ihre Auswirkungen jedoch auf irgendeine Weise kompensiert werden. Die Kompensierung ähnelt dem Rollback mit dem Unterschied, dass bei einer Kompensierung der Entwickler einen Code schreiben muss, um die durchgeführte Arbeit zu kompensieren. Nicht alle Transaktionsaufgaben müssen kompensierbar sein. So benötigen beispielsweise Aufgaben, die effektiv durch ACID-Transaktionen repräsentiert werden können, keine Kompensierung. Tatsächlich unterstützt die TransactionScope-Aktivität keine Kompensierung.
Abbildung 7 zeigt einen Beispielworkflow, der einige kompensierbare Transaktionsbereiche verwendet. Der Geschäftsprozess modelliert den Lebenszyklus einer Bestellung. Eine Bestellaufgabe hat zur Folge, dass die Kreditkarte des Kunden belastet (Scope_ChargeCreditCard-Aktivität) und danach Geld auf das Bankkonto des Anbieters eingezahlt wird (Scope_payorder-Aktivität). Beide Aktivitäten sind transaktional und kompensierbar. Angenommen, später im Workflow wird eine Ausnahme ausgelöst, weil beispielsweise das bestellte Produkt nicht mehr erhältlich ist. An dieser Stelle wird eine Geschäftsausnahme ausgelöst, und die gesamte bisher geleistete Arbeit muss rückgängig gemacht werden. Die Auswirkungen aller übermittelten ACID-Transaktionen und Nichttransaktionssequenzen von Aktivitäten müssen kompensiert werden. Jede kompensierbare Transaktions- oder Sequenzaktivität beinhaltet Logik, die nur dem Zweck dient, die ganze Arbeit oder einen Teil der Arbeit rückgängig zu machen.
Abbildung 7 Kompensierbare Transaktionen 
In Windows Workflow Foundation definieren Sie den Code zum Rückgängigmachen für jede kompensierbare Aktivität, indem Sie die Ansicht der Aktivität in die Kompensationshandleransicht umschalten (siehe Abbildung 8). In der Kompensationshandleransicht listen Sie alle Aktivitäten auf, die ausgeführt werden müssen, um die Auswirkungen der Operation rückgängig zu machen.
Abbildung 8 Hinzufügen von Kompensierungslogik zu Workflowsequenzen (Klicken Sie zum Vergrößern auf das Bild)
Das Konzept der Kompensierung ähnelt dem Konzept des Rollbacks. Während jedoch der Rollback eine reine Transaktionsoperation ist, betrifft die Kompensierung sowohl Transaktions- als auch Transaktionssequenzen von Operationen. Wenn Sie Abbildung 7 und Abbildung 8 miteinander vergleichen, wird Ihnen auffallen, dass sich die Benutzeroberflächen für die beiden Bereiche der kompensierbaren Transaktionen leicht unterscheiden. In Abbildung 8 sehen Sie statt des direkten Codes den Kompensierungscode.

Kompensierung
Eine gute Frage wäre: Wozu muss man sich eigentlich die Mühe machen, eine Kompensierung durchzuführen? Wäre eine einzige große ACID-Transaktion mit automatischem Rollback nicht genauso gut? Eine ACID-Transaktion ist am besten geeignet, wenn Operationen innerhalb derselben Datenbank oder innerhalb desselben Informationssystems stattfinden. ACID-Transaktionen eignen sich auch am besten, wenn Operationen schnell abgeschlossen werden. Wenn verschiedene Unternehmen und Dienste beteiligt sind, ist es oft schwierig, den Prozess in Begriffen der ACID-Semantik zu definieren. Für einen isolierten und dauerhaften Prozess müssen alle Ressourcen der verschiedenen Unternehmen für den Zeitraum, den die Aufgabe in Anspruch nimmt, gesperrt werden. Dies ist oft nicht sinnvoll, vor allem dann, wenn die Aufgabe lang dauert. Damit der Prozess konsistent und atomar ist, ist Ad-hoc-Kompensierungscode erforderlich.
Eine weitere gute Frage könnte lauten: Wer löst den Kompensierungscode eigentlich aus? Mit Fehlerbehandlung behandeln Sie eine oder mehrere Ausnahmen, die vom Workflow ausgelöst werden. Der Handler derartiger Ausnahmen ist eine weitere spezielle Compensate-Aktivität (siehe Abbildung 9). Die Compensate-Aktivität löst den Code aus, der Einschränkungen und Geschäftsregeln behebt, die durch eine Geschäftsausnahme gefährdet werden. Sie müssen die Compensate-Aktivität an eine kompensierbare Aktivität im Workflow binden. Dies erreichen Sie mithilfe der TargetActivityName-Eigenschaft im Workflow Extensions-Designer von Visual Studio 2005. Wenn Sie die TargetActivityName-Eigenschaft auf den Namen einer bestimmten Transaktion oder Sequenz einstellen, wird lediglich diese Transaktion oder Sequenz kompensiert. Sie können mehrere Compensate-Aktivitäten hinzufügen und die Reihenfolge und die Granularität festlegen, in denen die Kompensierung erfolgen soll. Wenn Sie nur Kompensierungslogik ausführen möchten, die die Rollbacklogik einer klassischen ACID-Transaktion nachahmt, dann binden Sie die Compensate-Aktivität an den ganzen Workflow. In diesem Fall wird die Kompensierung ab dem Ausnahmepunkt von unten nach oben bis zum Stamm des Workflows durchgeführt.
Abbildung 9 Compensate-Aktivität 
Durch Kompensierungscode können zusätzliche ACID-Transaktionen gestartet werden, um die Auswirkungen des Workflows auszugleichen. Darüber hinaus kann die Kompensierung auch fehlschlagen. Um sich abzusichern, müssen Sie daher auch für den Kompensierungscode geeignete Ausnahmehandler vorbereiten.

Leistung und Workflowtransaktionen
Einer der Faktoren, der sich am stärksten auf die Leistung eines Windows Workflow Foundation-Workflows auswirkt, ist die Persistenz. Ein Workflow kann mehrere Persistenzpunkte besitzen, von denen einige möglicherweise von Programmierern explizit definiert und andere durch integrierte und benutzerdefinierte Aktivitäten implizit erforderlich gemacht werden. Der Persistenzdienst wird automatisch aufgerufen, wenn der Workflow untätig wird, wenn für die Workflowinstanz die Unload-Methode aufgerufen wird oder wenn eine mit dem PersistOnClose-Attribut versehene Aktivität abgeschlossen wird. Die beiden in Windows Workflow Foundation integrierten Transaktionsaktivitäten „TransactionScope“ und „CompensatableTransactionScope“ erfordern Persistenz beim Abschließen. Daher sind für die Leistung ein effektiver Persistenzdienst und, was noch wichtiger ist, eine effektive Laufzeitumgebung für diesen Dienst von größter Bedeutung.
Ein Transaktionsworkflow stellt hinsichtlich seines Laufzeitmechanismus zwei Anforderungen: Persistenz und Transaktionsunterstützung. Standardmäßig wird die Dienstklasse „DefaultWorkflowCommitWorkBatchService“ verwendet, um Transaktionen über die .NET TransactionScope-Klasse abzuwickeln. Auf diese Weise wird dynamische Eskalation zu DTC automatisch und nur dann bereitgestellt, wenn dies erforderlich ist. Es gibt jedoch ein spezielles Szenario, in dem Sie im Interesse besserer Leistung nicht den Standarddienst verwenden sollten.
Wenn Sie den standardmäßigen Persistenzdienst verwenden, müssen Sie eine richtige Datenbank erstellen. Dies kann eine SQL Server 2005-Datenbank oder eine SQL Server 2000-Datenbank sein. Die Standarddatenbank enthält auch Tabellen und gespeicherte Prozeduren für den Standardüberwachungsdienst – die SqlTrackingService-Klasse. Stellen Sie sich ein Szenario vor, in dem beide Dienste aktiviert sind und gemeinsam dieselbe Datenbank nutzen, was bedeutet, dass sie genau dieselbe Verbindungszeichenfolge verwenden. Persistenz- und Überwachungsdaten werden stets innerhalb derselben Transaktion geschrieben. Wenn es sich jedoch um eine SQL Server 2000-Datenbank handelt, findet eine Eskalation zu DTC statt, es sei denn, Sie lassen die Dienste gemeinsam dasselbe Verbindungsobjekt nutzen. Wenn die Persistenz- und Überwachungsdaten in dieselbe SQL Server 2000-Datenbank geschrieben werden sollen, sollten Sie daher statt des Standarddiensts den Dienst „SharedConnectionWorkflowCommitWorkBatchService“ verwenden. Sie fügen diese Dienste wie hier gezeigt der Workflowlaufzeit hinzu:
workflowRuntime.AddService(
    new SqlWorkflowPersistenceService(connString)); 
workflowRuntime.AddService(
    new SqlTrackingService(connString));
workflowRuntime.AddService(
    new SharedConnectionWorkflowCommitWorkBatchService(connString));
Der Dienst „SharedConnectionWorkflowCommitWorkBatchService“ optimiert die Workflowleistung nur in diesem speziellen Szenario, da es die Gemeinkosten für zusätzliche Verbindungen zur Datenbank und DTC-Transaktionen vermeidet. Wenn Ihr transaktionsorientierter Workflow keine Überwachung erfordert oder für Überwachung und Persistenz verschiedene Datenbanken verwendet, sind Sie besser damit beraten, zur Transaktionsunterstützung den Standarddienst beizubehalten.
Am Rande sollte ich darauf hinweisen, dass der Überwachungsdienst und der Persistenzdienst stets innerhalb derselben Transaktion operieren. Der Überwachungsdienst besitzt eine boolesche Eigenschaft namens „IsTransactional“. Diese Eigenschaft auf „false“ einzustellen, macht den Überwachungsdienst keineswegs nichttransaktional. Dies bedeutet ganz einfach nur, dass Überwachungsdaten gerade rechtzeitig gespeichert werden, wenn die entsprechende Methode aufgerufen wird. Wenn „IsTransactional“ den Wert „true“ (Standardeinstellung) hat, fügt die TrackData-Methode des Diensts dem Arbeitsstapel Daten hinzu. Der Arbeitstapel wird anschließend am nächsten Persistenzpunkt im Workflow geleert.

Schlussbemerkung
Es ist tatsächlich recht einfach, Transaktionsaufgaben in Windows Workflow Foundation zu modellieren. Wenn Sie eine ACID-Transaktion (d. h. eine Transaktion, die atomar, konsistent, isoliert und dauerhaft ist) benötigen, die rasch eine Antwort generiert, sollten Sie die TransactionScope-Aktivität verwenden. Innerhalb dieser Aktivität setzen Sie weitere Aktivitäten zusammen, die mit der zugrunde liegenden Umgebungstransaktion auf eingetragene Objekte zugreifen. Wenn Sie dagegen eine Reihe lose gekoppelter Dienste orchestrieren müssen, um eine Aufgabe zu modellieren, die bis zu ihrem Abschluss einige Zeit in Anspruch nehmen kann, dann eignet sich dafür CompensatableTransactionScope besser.
Bedenken Sie dabei auch, dass mit einer Transaktion aufgrund der erforderlichen Persistenz und der Speicheranforderungen zusätzliche Kosten verbunden sind. Wenn Sie nur Kompensierung benötigen, sollten Sie die einfachere CompensatableSequence-Aktivität auswählen.

Senden Sie Fragen und Kommentare für Dino Espositio in englischer Sprache an  cutting@microsoft.com.


Dino Esposito ist Mentor bei Solid Quality Learning und Autor von Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Er lebt in Italien und ist ein weltweit gefragter Referent bei Branchenveranstaltungen. Sie können Esposito über cutting@microsoft.com erreichen oder dem Weblog unter weblogs.asp.net/despos beitreten.

Page view tracker