Autor Beitrag
Vegeto
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Mi 16.10.13 10:52 
Hallo,

es tut mir leid, dass ich euch damit nerve, aber ich komme einfach nicht mehr weiter :'( :'(
Seit zwei Tagen versuche ich dieses Problem zu lösen, doch ohne Erfolg :( :(

Ich habe schon zwei Themen eröffnet, wo meine Problem "gelöst" war, doch nun ein neues Problem (Thema 1 und Thema 2).

Ich bin gerade dabei mehre Aktionen auf meiner Datenbank durchzuführen, diese möchte ich mittels Transaction überwachen. Da ich mehrere Connections habe wollte ich zunächst TransScoup benutzen. Doch dort kam es immer wieder zu Fehlermeldung, dass die Transaktion schon benutzt wurde :/ diese konnte ich nicht beheben.

Dann dachte ich mir versuche ich verschide Transaktionen zu erstellen, so wie es mir Ralf Jansen gezeigt hat. Doch das Problem ist ich benutze zunächst ein DataReader um Datensätze in Variablen zu speichern und diese dann erst einzufügen, ich zeig euch mal Code, damit ihr versteht wie ich es meine...

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
string conn1 ="meinconn1", conn2 ="meinconn2";

SqlConnection deleteConn = new SqlConnection(conn1);
SqlConnection insert1Conn = new SqlConnection(conn2);
SqlConnection insert1ZielConn = new SqlConnection(conn1);
SqlConnection insert2Conn = new SqlConnection(conn2);
SqlConnection insert2ZielConn = new SqlConnection(conn1);
SqlConnection updateConn = new SqlConnection(conn1);
SqlConnection updateZielConn = new SqlConnection(conn1);

deleteConn.Open();
insert1Conn.Open();
insert1ZielConn.Open();
insert2Conn.Open();
insert2ZielConn.Open();
updateConn.Open();
updateZielConn.Open();

deleteTransaction = deleteConn.BeginTransaction();
insert1Transaction = insert1Conn.BeginTransaction();
insert1toZielTransaction = insert1ZielConn.BeginTransaction();
insert2Transaction = insert2Conn.BeginTransaction();
insert2toZielTransaction = insert2ZielConn.BeginTransaction();
updateTransaction = updateConn.BeginTransaction();
updateURLZielTransaction = updateZielConn.BeginTransaction();

SqlCommand deleteCMD = new SqlCommand();
deleteCMD.Transaction = deleteTransaction;
deleteCMD.Connection = deleteConn;

SqlCommand insert1CMD = new SqlCommand();
insert1CMD.Transaction = insert1Transaction;
insert1CMD.Connection = insert1Conn;
SqlCommand insert1ZielCMD = new SqlCommand();
insert1ZielCMD.Transaction = insert1toZielTransaction;
insert1ZielCMD.Connection = insert1ZielConn;

SqlCommand insert2CMD = new SqlCommand();
insert2CMD.Transaction = insert2Transaction;
insert2CMD.Connection = insert2Conn;
SqlCommand insertFCZielCMD = new SqlCommand();
insert2ZielCMD.Transaction = insert2toZielTransaction;
insert2ZielCMD.Connection = insert2ZielConn;

SqlCommand updateCMD = new SqlCommand();
updateCMD.Transaction = updateTransaction;
updateCMD.Connection = updateConn;
SqlCommand updateZielCMD = new SqlCommand();
updateZielCMD.Transaction = updateURLZielTransaction;
updateZielCMD.Connection = updateZielConn;


SO fängt mein Code an, es wird alles zunächst zugewisen, jetzt arbeite ich in einzelnen #region die Punkte ab:
BSP zeig ich euch jetzt den INSERT 2
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
#region Insert 2

                insert2CMD.CommandTimeout = 240;

                insert2CMD.CommandText =
                    "SELECT-QUERY";

                using (SqlDataReader reader2 = insert1CMD.ExecuteReader())
                {
                    while (reader2.Read())
                    {
                       if (!reader2.IsDBNull(0))
                            strE = Convert.ToString(reader2[0]);
                        else
                            strE = "";
                        if (!reader2.IsDBNull(1))
                            strAcc = Convert.ToString(reader2[1]);
                        else
                            strAcc = "";
                        intAcc = int.Parse(strAcc.Trim().Trim('I'));

                        if (!reader2.IsDBNull(2))
                            strKun = Convert.ToString(reader2[2]);
                        else
                            strKun = "";
                        if (!reader2.IsDBNull(3))
                            strKon = Convert.ToString(reader2[3]);
                        else
                            strKon = "";

                        #region ZielDatenbank einfügen

                        insert2ZielCMD.CommandTimeout = 240;
                        insertFCZielCMD.CommandText = "INSERT - QUERY";

                        if (strE == "")
                            insert2ZielCMD.Parameters.AddWithValue("@e", DBNull.Value);
                        else
                            insert2ZielCMD.Parameters.AddWithValue("@e", strE );

                        if (intAcc == 0)
                            insert2ZielCMD.Parameters.AddWithValue("@kun", DBNull.Value);
                        else
                            insert2ZielCMD.Parameters.AddWithValue("@kun", intAcc );

                        if (strKun == "")
                            insert2ZielCMD.Parameters.AddWithValue("@kunde", DBNull.Value);
                        else
                            insert2ZielCMD.Parameters.AddWithValue("@kunde", strKun );

                        if (strKon == "")
                            insert2ZielCMD.Parameters.AddWithValue("@kon", DBNull.Value);
                        else
                            insert2ZielCMD.Parameters.AddWithValue("@kon", strKon );

                        insert2ZielCMD.ExecuteNonQuery();

                        #endregion
                    }
                }

                #endregion


Die variablen erstelle ich ausserhalb der Methode, sodass sie immer nur neu gefüllt werden.
Doch wenn ich das jetzt so ausführe, bleibt er beim insert2ZielCMD.ExecuteNonQuery() hängen, er bleibt da solange bis die Exception kommt, dass ein Command-Timeout passiert ist!

Von solchen Region habe ich 3 stück, dort lade ich die Datensätze in Variable und passe sie an und dann werden sie mittels Insert-Query zurück geschrieben.

Das packe ich in ey try und am ende des try mache ich ein commit(), was aber nie erreicht wird.

Ich hoffe jemand kann mir helfen.

Lg

PS.: Wenn ich das so aufbaue wie Ralf Jansen mir das in Thema 1 gezeigt hat, muss ich die Transaction mit unterschiedlichen Connection in die schleife packen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 16.10.13 11:29 
Du baust dir vermutlich üble Deadlocks in denen du gleichzeitig verschiedene Transaktionen auf die gleichen Daten verwendest und die auch noch änderst. Wäre vielleicht gerade der richtige Zeitpunkt für dich mal das Thema "Transaction Isolation Level" in der Hilfe nachzulesen. Tipp du benutzt gerade "Read Commited" (den Default) das bedeutet das Daten die in der Transaktion angefasst wurden solange von anderen Transaktionen nicht lesbar sind bis die Transaktion beendet sind . Wenn man dann so wie du mit Transaktionen um sich wirft ist die Wahrscheinlichkeit groß dass Transaktionen gegenseitig endlos aufeinander warten. Ein Deadlock halt.

Für diesen Beitrag haben gedankt: Vegeto
Vegeto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Mi 16.10.13 13:12 
Hallo Ralf Jansen,

du solltest dir überlegen(wenn du es noch nicht bist) Berufsschullehrer zu werden ;)

Habe gerade etwas zum Thema "Transaction Isolation Level" gelesen.
Habe dann meine Transactions alle in IsolationLevel.ReadUncommitted geändert, doch leider hilft es nicht weiter. Nach einmalig durchführen(wo alles klappt), wenn ich es dann wieder ausführen möchte bleibt er beim ExecuteNonQuery wieder hängen :(
Ich habe sogar versucht die Transaction im finally block mit dispose vollstäntig frei zu geben, doch nützen tut es nichts :(

Doch mir ist was aufgefallen:
Wenn ich im SSMS die Ziel Tabelle lösche und mit anderen Daten speichere( ich mache ein delte und dann in insert im SSMS), dann wieder die Anwendung laufen lasse, macht er genau das was ich will, doch wenn ich bei der Anwendung wieder klicke, tut er es nicht und bleibt hängen :(

Hoffe jemand kann mir weiter helfen.

Lg
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 16.10.13 14:01 
Das etwas in der Datenbank blockiert ist nur eine Annahme.

Um die zu prüfen kannst du versuchen während dein ExecuteNonQuery hängt den Transaktionszustand und wer wenn blockiert in der Datenbank zu beobachten.
Hilfreich ist zum Beispiel die sp_who2 Stored Procedure die du mal ausführen kannst. In der Command Spalte solltest du die von dir abgesetzten Commands wiedererkennen und die BlkBy Spalte zeigt dir an welcher Task gerade welchen anderen blockiert. Beim von mir erwarteten Deadlock solltest du sowas sejen wie SPID 4 wird von der 5 blockiert und die 5 von der 4. Die beiden Commands in den Task sollten dir dann verdeutlichen was in deiner Anwendung warum schief geht.


Edit : Alternativ kannst du auch den Aktivitätsmonitor im Management Studio starten und die Prozesse beobachten das entspricht in etwas der Ausgabe von sp_who2.

Für diesen Beitrag haben gedankt: Vegeto
Vegeto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Mi 16.10.13 14:36 
Hallo Ralf Jansen,

ich habe mir jetzt beide angeguckt, fand aber das der Aktivitätsmonitor übersichtlicher ist :D

Du hattest Recht!
Auf der Datenbank blockieren sich die einzelnen Transaktion, bei dem einem Insert-Query Stand zum Beispiel unter Aktivitätsmonitor das er von einem Select-Query blockiert ist.

Wie kann ich das jetzt lösen?
Ich habe schon auf ReadUncommitted gestellt, hat aber nichts gebracht.

Das komische ist ja das, wenn ich über SSMS die Tabelle lösche und irgendwelche datensätze einfüge und dann die Anwendung laufen lasse, dass es funktioniert.
Habe auch hier im Aktivitätsmonitor nach gegcukt und dort wird dann nichts von irgendwas blockiert.

Ich mein er kann es ja nicht erst ausführen und beim zweiten mal nicht mehr ausführen.

hoffe jmd kann mir helfen.

lg

EDIT: Habe jetzt sogar alle Select - Statements mit NOLOCK versehen und alle Transaction wo Selektiert und an SQLDataReader gegeben wird habe ich auf IsolationLevel.ReadUncommitted geändert.
Doch immernoch steht im Aktivitätsmonitor das ein Statement das andere blockiert... BITTE HELFT MIR
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 16.10.13 17:14 
Zitat:
Auf der Datenbank blockieren sich die einzelnen Transaktion, bei dem einem Insert-Query Stand zum Beispiel unter Aktivitätsmonitor das er von einem Select-Query blockiert ist.


Nur blockieren wäre noch nicht schlimm dann wartet die eine Transaktion halt bis die andere fertig ist. Da sollte iegentlich nie ein Timeout kommen außer eine Operation dauert tatsächlich ewig. Aber wenn du einen Zirkelschluss bekommst ala A wartet auf B udn B wartet auf A oder komplizierter über noch mehr Ecken dann solltest du genauer hinschauen.

Aber wieso sind das 2 Transaktionen? Wenn du was ließt um es dann zu ändern sollte es eine sein. Und wenn du schon mehrere Transaktionen benutzt dann öffne sie erst dann wenn du die auch brauchst und schließe die sofort wenn du die nicht mehr brauchst. Und wenn du jetzt sagst aber ich weiß noch nicht das ich die Transaktion commiten oder rollbacken darf das hängt davon was die andere(n) Transaktion(en) machen ist das ein klares Zeichen dafür das das nicht mehrere Transaktionen sein sollten.

Für diesen Beitrag haben gedankt: Vegeto
Vegeto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Do 17.10.13 08:26 
Hallo Ralf Jansen,

laut dem Aktivitätenmonitor blockiert nur eine Abfrage die andere, sprich dort steht "nur" das Sitzungs ID 56, von Sitzungs ID 55 blockiert wird.
Das heißt die Operation verfängt sich dort.

Doch wie schon gesagt, wenn ich im SSMS die Ziel Tabelle mittels Delete From zieltabelle eingebe und dann die Anwendung laufen lasse, dann kommt es zu keinem Fehler. Echt komisch.

Warum es zwei sind kann ich dir sagen, also ich benutze ja die eine Transaktion um Daten zu holen(select), danach verarbeite ich diese und in einem neuem Command (in der Schleife) kommt die nächste transaction um die neuen Daten in die ZielTabelle zu speichern(insert).
Das problem beim Commiten des Insert-Query füge ich ja neue Datensätze hinzu, aber sollte alles ordnunggerecht ablaufen, wird später eine delete comitted und dann wird die tabelle gelöscht.

einzige möglichkeit die ich hier sehe, sage wenn ich falsch liege, ich hole die Daten in ein Datatable und von da aus mittels parameter und command insert in ziel tabelle???

Und leider ist es so dass die Transaction abhängig voneinander sind.

Lg
Vegeto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Do 17.10.13 10:23 
Hallo...

Ich habe mein Problem nun gelöst :)

Ich habe es mit Datatables gelöst, dafür habe ich zunächst alle relevanten Daten(select-Query in SqlDataReader) in eine DataTable gepackt, danach habe ich eine Transaktion geöffnet wo er alle Daten über nur eine Connection macht :) und dort arbeite ich mit foreach-schleifen(zum auslesen des Datatables) speichere die Werte in Variablen und die gebe ich mittels Command Parameter weiter.

Ich möchte mir hier bei Ralf Jansen noch einmal ganz Herzlichst bedanken :lol: :D
habe auch durch dich mehr dazu gelernt :idea:

Naja wünsche euch noch einen schönen Tag :)

Lg