Was sind die besten Praktiken zur Durchführung von Transaktionen in C# .Net 2.0. Welche Klassen sollten verwendet werden? Was sind die Fallstricke, auf die man achten sollte usw. All dieses Commit- und Rollback-Zeug. Ich fange gerade ein Projekt an, bei dem ich möglicherweise einige Transaktionen durchführen muss, während ich Daten in die DB einfüge. Alle Antworten oder Links, auch zu grundlegenden Dingen über Transaktionen, sind willkommen.
Antworten
Zu viele Anzeigen?Es gibt 2 Hauptarten von Transaktionen: Verbindungstransaktionen und Umgebungstransaktionen. Eine Verbindungstransaktion (z.B. SqlTransaction) ist direkt an die Datenbankverbindung (z.B. SqlConnection) gebunden, was bedeutet, dass Sie die Verbindung immer wieder weitergeben müssen - das ist in einigen Fällen in Ordnung, erlaubt aber keine "create/use/release"-Nutzung und erlaubt auch keine datenbankübergreifende Arbeit. Ein Beispiel (aus Platzgründen formatiert):
using (IDbTransaction tran = conn.BeginTransaction()) {
try {
// your code
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
Nicht zu unübersichtlich, aber auf unsere Verbindung "conn" beschränkt. Wenn wir andere Methoden aufrufen wollen, müssen wir jetzt "conn" weitergeben.
Die Alternative ist eine umgebende Transaktion; neu in .NET 2.0 ist die TransactionScope Objekt (System.Transactions.dll) ermöglicht die Verwendung für eine Reihe von Operationen (geeignete Anbieter werden automatisch in die umgebende Transaktion aufgenommen). Dies erleichtert die Nachrüstung von bestehendem (nicht transaktionalem) Code und die Kommunikation mit mehreren Anbietern (obwohl sich DTC einschalten wird, wenn Sie mit mehr als einem Anbieter kommunizieren).
Zum Beispiel:
using(TransactionScope tran = new TransactionScope()) {
CallAMethodThatDoesSomeWork();
CallAMethodThatDoesSomeMoreWork();
tran.Complete();
}
Beachten Sie hier, dass die beiden Methoden ihre eigenen Verbindungen handhaben können (öffnen/verwenden/schließen/entsorgen), jedoch werden sie stillschweigend Teil der umgebenden Transaktion, ohne dass wir etwas übergeben müssen.
Wenn Ihr Code fehlerhaft ist, wird Dispose() ohne Complete() aufgerufen, so dass er zurückgesetzt wird. Die erwartete Verschachtelung usw. wird unterstützt, obwohl Sie eine innere Transaktion nicht zurücksetzen können, ohne die äußere Transaktion abzuschließen: Wenn jemand unglücklich ist, wird die Transaktion abgebrochen.
Ein weiterer Vorteil von TransactionScope ist, dass es nicht nur an Datenbanken gebunden ist; jeder transaktionsfähige Anbieter kann es verwenden. WCF, zum Beispiel. Oder es gibt sogar einige TransactionScope-kompatible Objektmodelle (d.h. .NET-Klassen mit Rollback-Fähigkeit - vielleicht einfacher als ein Memento, obwohl ich diesen Ansatz selbst nie verwendet habe).
Alles in allem ein sehr, sehr nützliches Objekt.
Einige Vorbehalte:
- Auf SQL Server 2000 geht ein TransactionScope sofort zu DTC; dies ist in SQL Server 2005 und höher behoben, es kann die LTM (viel weniger Overhead) verwenden, bis Sie mit 2 Quellen usw. sprechen, wenn es zu DTC erhöht wird.
- Es gibt eine Glitch das bedeutet, dass Sie möglicherweise Ihre Verbindungszeichenfolge anpassen müssen
protected void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
{
connection1.Open();
// Start a local transaction.
SqlTransaction sqlTran = connection1.BeginTransaction();
// Enlist a command in the current transaction.
SqlCommand command = connection1.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute two separate commands.
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
command.ExecuteNonQuery();
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
command.ExecuteNonQuery();
// Commit the transaction.
sqlTran.Commit();
Label3.Text = "Both records were written to database.";
}
catch (Exception ex)
{
// Handle the exception if the transaction fails to commit.
Label4.Text = ex.Message;
try
{
// Attempt to roll back the transaction.
sqlTran.Rollback();
}
catch (Exception exRollback)
{
// Throws an InvalidOperationException if the connection
// is closed or the transaction has already been rolled
// back on the server.
Label5.Text = exRollback.Message;
}
}
}
}
Es kommt auch darauf an, was Sie brauchen. Für einfache SQL-Transaktionen können Sie versuchen, TSQL-Transaktionen durchzuführen, indem Sie BEGIN TRANS und COMMIT TRANS in Ihrem Code verwenden. Das ist der einfachste Weg, aber er ist komplex und Sie müssen darauf achten, dass die Übergabe richtig erfolgt (und ein Rollback erfolgt).
Ich würde etwas verwenden wie
SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
...
Do SQL stuff here passing my trans into my various SQL executers
...
trans.Commit // May not be quite right
}
Jeder Fehler wird Sie direkt aus dem using
und die Transaktion wird immer übertragen oder zurückgesetzt (je nachdem, was Sie ihr sagen). Das größte Problem, das wir hatten, war, sicherzustellen, dass die Transaktion immer übertragen wird. Die Verwendung stellt sicher, dass der Umfang der Transaktion begrenzt ist.