Laden...

TRansactionsproblem

Erstellt von tommispilot vor 15 Jahren Letzter Beitrag vor 15 Jahren 1.787 Views
T
tommispilot Themenstarter:in
28 Beiträge seit 2008
vor 15 Jahren
TRansactionsproblem

<MS SQL Server 2005>

Hallo,

Folgendes Szenario:

Dei Transaction einer Instanz A läuft (Schreibzugriff auf einen DS)
und ist noch nicht abgeschlossen.

Nun versucht eine Instanz B einen Lesezugriff.
Eine Exception wird für diesen Lesezugriff abgefangen.
Der User bekommt eine Nachricht, wie zb.

"Der Datensatz kann im Moment nur gelesen, nicht aber bearbeitet werden. Möglicherweise werden Ihnen gerade veraltete Daten angezeigt. Falls Sie den DS bearbeiten möchten, versuchen Sie es zu einem späteren Zeitpunkt noch einmal!".

Nun soll der Leseprozess B aber den ursprünglichen Wert anzeigen, also den Wert, welcher in der Datenbank aktuell steht und NICHT den Wert, welcher von Instanz A versucht wurde zu schreiben.

Wie mache ich das???

Bisher:
Code A:


String wertUpdate = txt_input.Text;
TimeSpan scopeTimeout = new TimeSpan(0, 0, 30, 0, 0);
transactionScope = new TransactionScope(TransactionScopeOption.Required, scopeTimeout);
ConnectionOpen(); 

txt_output.Text = Lesen();
Schreiben(wertUpdate);
txt_output.Text = Lesen();

Code B:


TransactionOptions transOptions = new TransactionOptions();
transOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
try
{
TimeSpan scopeTimeout = new TimeSpan(0, 0, 30, 0, 0);
transactionScope = new TransactionScope(TransactionScopeOption.Required,   scopeTimeout);
				
ConnectionOpen();

txt_output.Text = Lesen();
}
catch (SqlException ex)
{
ConnectionClose();
transactionScope.Dispose();
transactionScope = null;

//IsolationLevel: ???????
transOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, transOptions);

ConnectionOpen();

MessageBox.Show("...");

txt_output.Text = Lesen();

}
finally
{
ConnectionClose();
transactionScope.Dispose();
transactionScope = null;
}

Vielen Dank für's reinschauen.

84 Beiträge seit 2008
vor 15 Jahren

Hallo!

Bin mir jetzt unsicher mit welchem Verbindungstyp du arbeitest, aber wenn ich mit Transaktionen arbeite sieht das in etwa so hier aus:


using System.Data.SqlClient;

// ...

SqlConnection verbindung = new SqlConnection("connectionString wie auch immer");
verbindung.Open();

SqlTransaction transaktion = verbindung.BeginTransaction();

// Sämtlicher Krimskrams den du machen möchtest...

transaktion.Commit();
// oder auch "transaction.Rollback()

verbindung.Close()

So kann deine Anwendung A schreiben und Anwendung B kann sich die ursprünglichen Daten angucken.

Das funktioniert natürlich nicht nur für die SqlConnection sonder von allen Connection's die von System.Data.Common.DbConnection veerbt wurde.

Vielleicht hilfts.

Gruß!

T
tommispilot Themenstarter:in
28 Beiträge seit 2008
vor 15 Jahren

Danke, werd ich mal probieren.

T
tommispilot Themenstarter:in
28 Beiträge seit 2008
vor 15 Jahren

Ich frag doch noch mal:
Geht's auch irgendwie ohne "SqlTransaction"?

Dafür gibt es ja jetzt "TransactionScope", so das man kein "SqlTransaction"-Objekt
mehr braucht, oder mache ich da einen Denkfehler?

X
1.177 Beiträge seit 2006
vor 15 Jahren

Huhu,

ich komm mit deiner Beschreibung nicht klar 🙂

Funktioniert nun der Lesezugriff oder nicht? 🤔

Prinzipiell wird alles was du an den SqlServer schickst in einer Transaktion verpackt, nur diese wird dann eben explizit Commited. Was mich eher interessiert: wieso hast Du Updates die so lange dauern, dass du dem User einen Fehler beim lesen präsentieren musst?

Dein Problem lässt sich entweder mit dem Isolation-Level beheben, oder Du musst auf ein eigenes Locking zurückgreifen. Je nach Anforderung deiner App.

🙂

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

84 Beiträge seit 2008
vor 15 Jahren

Hi,

also sag mal was du als eigentliche Verbindung nutzt? (OdbcConnection, OleDbConnection, SqlConnection, ...)

Ich hab mir jetzt nur mal kurz angeguckt was System.Transaction so zu bieten hat.
(Für alle die auch auf der Suche waren 🙂 "%SystemRoot%\Microsoft.net\Framework\v2.0.50727\System.Transactions.dll")
Ich würde behaupten dass das eigentliche Transaction-Objekt das du brauchst einfach System.Transactions.Transaction ist. Mit dem kann man wohl auch Commit() und Rollback() machen.

++EDIT: ++ Kein Commit!

Du brauchst auf jeden Fall erstmal ne Zuweisung dass die Transaction zu der eigentlichen Verbindung gehört bzw. von ihr stammt.
Wie zum Beispiel hier:


SqlTransaction transaktion = verbindung.BeginTransaction();

In deinem Code geht nichts dergleichen hervor.
Kann schon sein dass das TransactionScope was sinnvolles kann, nur woher weiß deine Datenbank dass es das TransactionScope-Objekt überhaupt gibt?
(Wenn ich mich verguckt habe oder was falsch verstanden hab korregier mich!)

Gruß

Matscher

4.207 Beiträge seit 2003
vor 15 Jahren

@ Matscher: Nein, sorry, das ist ziemlich falsch so.

Was Du brauchst ist der TransactionScope, mit dem Transaction-Objekt an sich macht man gar nichts per Hand.

Einen Commit musst Du explizit auslösen, mittels scope.Complete();, ein Rollback erfolgt automatisch, falls dieser Aufruf fehlt.

Und die Transaktion musst Du auch nicht zuweisen, das geschieht automatisch, sofern

a) die Connection innerhalb des TransactionScope aufgemacht wird
b) der .NET Data Provider das System vom System.Transactions unterstützt.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

84 Beiträge seit 2008
vor 15 Jahren

Ok... hab mit dem System.Transactions.TransactionOptions noch nie gearbeitet.
Wie ich auch gerade bemerke, habe ich im Code auch was übersehen. *Pfeif* 🙂
Dann kommts hier wohl drauf an was in ConnectionOpen passiert.

Ist das überhaupt das Problem?
@tommispilot: Willst du wissen wie man eine Transaktion startet oder willst du wissen wie man rausfindet das ein Datensatz bearbeitet wird / wurde?

4.207 Beiträge seit 2003
vor 15 Jahren

Um mal auf die ursprüngliche Frage zurückzukommen:

Das geht mit dem richtigen Isolationlevel.

ReadCommitted oder höher sollte korrekt sein.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

T
tommispilot Themenstarter:in
28 Beiträge seit 2008
vor 15 Jahren

Danke.

84 Beiträge seit 2007
vor 15 Jahren

Hallo Golo,

ich misch mich mal in die Diskussion mit ein. Ich bin ein Kollega von tommispilot und wir bauen an dem selben Problem. Bevor das ganze hier in eine total falsche Richtung läuft erst mal folgende Frage:
Wir haben ein Programm in dem auf einem Dialog viele viele Daten geändert werden können. Wir haben mehrere TabPages und SubDialoge in den Daten geändert werden können. Stell dir eine Kontaktverwaltung vor, mit unendlich vielen Zusatzdaten. Am Ende kann der Benutzer mit OK oder ABBRECHEN im Hauptdialog entscheiden ob er diese Daten in die DB schreiben will oder nicht.
Soweit das Szenario.
Nun dachten wir uns beim Öffnen des Dialog machen wir "BeginTransaction" mit diesem TransactionScope, dann kann der Benutzer Änderungen machen, die wir sofort in die DB schreiben, und wenn er am Ende ABBRECHEN drückt werden alle Änderungen durch ROLLBACK zurückgenommen, oder aber bei OK "Commited".

Kann oder sollte man für sowas überhaupt TransactionScope "missbrauchen" oder ist da eher interne Logic gefragt? Wäre so schön einfach 😁

Howard

4.207 Beiträge seit 2003
vor 15 Jahren

Zum Transaktionshandling kann man den TransactionScope schon benutzen, nur halte ich dieses Verfahren, so wie ihr es anstrebt, für ausgesprochen unglücklich.

Denn damit bekommt ihr langlaufende Transaktionen, die sich über Minuten oder sogar Stunden hinziehen können, und das ist nicht gut.

Was Ihr eigentlich braucht, ist ein vernünftiges Lockingverfahren (optimistisch oder pessimistisch). Macht Euch als erstes Mal darüber schlau, und welches davon Ihr einsetzen wollt.

Die konkrete Umsetzung kann dann mit TransactionScopes gemacht werden, aber eine einzige Transaktion um alles ist mit oder ohne TransactionScope eine eher blöde Idee.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

84 Beiträge seit 2007
vor 15 Jahren

Hey thx für die schnelle Antwort 👍

Also das mit dem dem Lockingverfahren ist ja noch ne ganz andere Nummer. Hier ging es zunächst einfach um diese Idee mit der "RiesenTransaction" um sich die doch eher aufwendige Logic zu sparen. Ich hab irgendwie sowas geahnt mit der "Dummen Idee" als ich mir deinen dnp-Artikel durchgelesen habe. 😁

Okay auch wenn hier nun alles mit den Händen über dem Kopf zusammenschlägt ABER: dumme Fragen gibt es nicht 😁

Sach ma kurz WARUM es ne blöde Idee ist ?(

Okay irgendwie ist die DB dann die ganze Zeit geblockt. Aber da dachten wir würden wir halt solange die "alten" Daten zu lesen bekommen......

Howard

3.825 Beiträge seit 2006
vor 15 Jahren

Hallo Tommi,

eine Tansaktion sollte nur so lange wie unbedingt nötig laufen, max. ein paar Sekunden. Auf keinen Fall sollte auf User-Eingaben während der Transaktion gewartet werden.

Zum Locking schau hier :

Pessimistisches Locking und Sperrtabellen

Wenn es komfortabel sein soll muss man da leider etwas Arbeit reinstecken.

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

4.207 Beiträge seit 2003
vor 15 Jahren

Es ist allein schon deshalb keine gute Idee, weil es Ressourcen in der DB verbraucht, und wenn da mehrere Leute gleichzeitig drauf arbeiten, muss das nicht sein.

Außerdem wird dann für die DB das Handeln des Transactionlogs unnötig aufwändig, was ebenfalls nicht sein muss.

Der korrekte Weg wäre:

Datensatz laden ohne Transaktion
Bearbeiten
Datensatz schreiben mit Transaktion

Das ganze halt mit optimistischem, pessimistischem oder pseudo-pessimistischem Locking, um gleichzeitige Bearbeitung zu handhaben.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

84 Beiträge seit 2007
vor 15 Jahren

Okay Ladys and Gentleman: vielen herzlichen für die Infos. Hört sich nach viel Arbeit an für uns jetzt aber okay das haben wir nu davon 😁

Howard

4.207 Beiträge seit 2003
vor 15 Jahren

Vielleicht noch als PS: Falls Ihr LINQ to SQL nutzt, bekommt Ihr optimistisches Locking frei Haus. Ziemlich gute Infos dazu gibt's in dem Buch "LINQ in Action".

Ansonsten wäre evtl der Blick auf die übrigen O/R-Mapper nicht ganz verkehrt ...

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

F
10.010 Beiträge seit 2004
vor 15 Jahren

Naja, selbst das einfache ADO.NET ist auf Optimistsches Locking gut eingestellt,
und der CommandBuilder erzeugt ja auch schon Cmds, die per Parallelitätsverletzung
eine Behandlung ermöglichen.

84 Beiträge seit 2007
vor 15 Jahren

also wir benutzen DbProviderFactories da wirds ja ähnliches geben oder?

Howard