Forums Neueste Beiträge
 

Design pattern fuer "nested transactions"

14/11/2008 - 15:23 von Heinrich Moser | Report spam
Hallo!

Das ist wahrscheinlich eine FAQ, aber leider blieb meine Suche
erfolglos. Ich versuche, ein gutes Design Pattern für "verschachtelte
Transaktionen" zu finden. Genau genommen möchte ich ein paar
Bibliotheksfunktionen schreiben, die es ermöglichen, Codeblöcke als
"muss innerhalb einer Transaktion ausgeführt werden" zu markieren, und
dann das ganze Transaktionshandling "automatisch" erledigen.

Ein Pseudocode-Beispiel zur Verdeutlichung ("..." macht irgendwas in
der Datenbank über die globaleConnection):


Sub MachEtwas()
Dim t = globaleConnection.BeginTransaction()
Try
...
t.Commit()
Catch ex As Exception
t.Rollback()
Throw ex ' hier geht leider die Stracktrace-Information
' verloren, aber das ist ein anderes Problem
End Try
End Sub


Nun wird MachEtwas sowohl "standalone" aufgerufen, als auch innerhalb
von bereits bestehenden Transaktionen. D.h. ich müsste soetwas in die
Richtung machen:


Sub MachEtwas(ByVal bereitsInTransaktion As Boolean)
Dim t As IDbTransaction
If Not bereitsInTransaktion Then t = globaleConnection.BeginTransaction()
Try
...
If Not bereitsInTransaktion Then t.Commit()
Catch ex As Exception
If Not bereitsInTransaktion Then t.Rollback()
Throw ex
End Try
End Sub


Das erfordert einen zusàtzlichen Parameter, der nichts mit der
Anwendungslogik zu tun hat, und blàht den Code auf; daher gefàllt mir
das nicht sonderlich. Im Augenblick probiere ich folgenden Ansatz:


Sub MachEtwas()
RequireTransaction()
Try
...
Finally
EndRequireTransaction()
End Try
End Sub


wobei der erste Aufruf von RequireTransaction einen BeginTransaction
ausführt und der letzte Aufruf von EndRequireTransaction die
Transaktion committed (Code stark vereinfacht):


Dim transactions As Integer = 0
Dim transaction As IDbTransaction

Shared Sub RequireTransaction()
If transactions = 0 Then transaction = globaleConnection.BeginTransaction()
transactions += 1
End Sub

Shared Sub EndRequireTransaction()
transactions -= 1
If transactions = 0 Then transaction.Commit()
End Sub


Das funktioniert ganz gut, kann aber natürlich mit Exceptions nicht
umgehen sondern committed immer die Transaktion. Ein Ansatz wàre, im
EndRequireTransaction mit Marshal.GetExceptionPointers zu erkennen, ob
der Finally-Block über eine Exception erreicht wurde oder nicht, aber
das kommt mir recht unsauber vor. Da ich mir denke, dass ich wohl
nicht der erste bin, der sich über dieses Problem Gedanken macht, bin
ich für alle Hinweise dankbar. Ich suche, wie gesagt, eine Lösung, die
das Transaktionshandling so weit wie möglich wegabstrahiert, um den
Anwendungscode übersichtlich zu halten.

LG,
Heinzi
 

Lesen sie die antworten

#1 Olaf Doschke
14/11/2008 - 19:52 | Warnen spam
Das funktioniert ganz gut, kann aber natürlich mit Exceptions nicht
umgehen sondern committed immer die Transaktion.



abgesehen von diesem Problem ist das ganze doch ein
Singleton pattern.

Verschachtelte Transaktionen wàren doch in diversen
Situationen besser angebracht, allerdings ist es nicht
unüblich z.B. über einen Rekursionslevel in Triggern
von Datenbanken abzuhandeln, ob eine Transaktion
gestartet oder in der bereits gestarteten weiter ge-
abeitet wird, z.B. bei kaskadierendem Löschen von
Daten, was, sobald das kaskadieren auf einen Fehler
làuft insgesamt zurückgesetzt werden soll.

eine Lösung für das Rollbackproblem ist anhand
einer Property/Variable Success zu entscheiden, ob ein
Commit oder Rollback nötig ist:

Dim transactions As Integer = 0
Dim transaction As IDbTransaction
Dim Success As Boolean = True

Shared Sub RequireTransaction()
If transactions = 0 Then transaction =
globaleConnection.BeginTransaction()
transactions += 1
End Sub

Shared Sub EndRequireTransaction()
If Success Then
transactions -= 1
If transactions = 0 Then transaction.Commit()
Else
transactions = 0
transaction.Rollback()
End If
End Sub

die Try/Catch Blöcke sàhen dann etwa so aus:

Try
...
Catch
Success = False
Finally
EndRequireTransaction()
End Try

Außerdem würden komplexere Routinene mit weiteren Subroutinen
sich dann jeweils immer an Success orientieren müssen, ob noch
weiter gemacht wird oder der Mißerfolg hocheskaliert zum
Finally auf oberster Ebene.

Tschüß, Olaf.

Ähnliche fragen