Announcement

Collapse
No announcement yet.

transaction und der SQLDataReader

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • transaction und der SQLDataReader

    Hallo!<br>

    <p>Ich benutzte ADO.net und möchte folgendes tun:
    <br>
    1. -> Transaction eröffnen<br>
    2. Einige Inserts schreiben<br>
    3. Select über ne Menge Daten schieben<br>
    4. Noch ein Insert schreiben<br>
    5. -> Transaction beenden<br>
    6. den Container mit dem Select auslesen<br>

    <p>Nun passiert aber folgendes: Das mit dem SQLDataReader funzt nicht. Schliesst der einfach meine Transaction?<br>

    <PRE>
    Console.WriteLine("Test");

    string myConnString="user id=root;password=root;initial catalog=ilmtest;data source=192.168.1.35\\VSdotNET;Connect Timeout=60;Integrated Security=SSPI";

    SqlConnection myConnection = new SqlConnection(myConnString);
    myConnection.Open();

    SqlCommand myCommand = new SqlCommand();
    SqlTransaction myTrans;

    myTrans = myConnection.BeginTransaction();

    myCommand.Connection = myConnection;
    myCommand.Transaction = myTrans;

    SqlDataReader myReader;

    try
    {
    myCommand.CommandText = "INSERT INTO tdatentypen (ztextid_fk, ztinyint, zsmallint, zint, zbigint, zfloat, zdouble, zvarchar, ztext) VALUES (1,3,2,44444,55555,0.1111, 0.2222, 'Kuckuk', 'Nasenbaer');";
    myCommand.ExecuteNonQuery();
    myCommand.CommandText = "INSERT INTO tdatentypen (ztextid_fk, ztinyint, zsmallint, zint, zbigint, zfloat, zdouble, zvarchar, ztext) VALUES (1,3,2,44444,55555,0.1111, 0.2222, 'Kuckuk', 'Nasenbaer');";
    myCommand.ExecuteNonQuery();
    myCommand.CommandText = "SELECT * FROM tdatentypen";
    myReader = myCommand.ExecuteReader(System.Data.CommandBehavio r.Default);
    myCommand.CommandText = "INSERT INTO tdatentypen (ztextid_fk, ztinyint, zsmallint, zint, zbigint, zfloat, zdouble, zvarchar, ztext) VALUES (1,3,2,44444,55555,0.1111, 0.2222, 'Kuckuk', 'Nasenbaer');";
    myCommand.ExecuteNonQuery();
    myReader.Close();


    myTrans.Commit();


    while (myReader.Read())
    {
    Console.WriteLine(myReader.GetString(8));
    }


    Console.WriteLine("Both records are written to database.");
    }
    catch(Exception e)
    {
    myTrans.Rollback();
    Console.WriteLine("Error: {1}", e.Message);
    Console.WriteLine("Error reported by {1}.", e.Source);
    Console.WriteLine("Neither record was written to database.");
    }
    finally
    {

    myConnection.Close();
    }
    </PRE>

    <p>Das Beispiel ist wenig sinnvoll. Aber meine eigentliche Anwendung schon. Nur wäre das hier zu lang .... deswegen diese Abstraktion. Vielleicht hat ja wer nen Tip.
    <br>

    Christian Zink

  • #2
    Hallo,

    irgendwie kann das auch theoretisch nicht funktionieren, wenn man das Sourcecode-Beispiel durch eine verbale Beschreibung ersetzt: <br>
    1. SqlDataReader-Instanz über ExecuteReader anfordern <br>
    2. SqlDataReader-Instanz über <b>Close</b> schließen <br>
    3. SqlDataReader-Instanz (die im Punkt 2 bereits <b>geschlossen</b> wurde) soll über Read die Daten lesen

    Außerdem würde ich<br>
    a) separate SqlCommand-Instanzen nutzen (um allein die Übersichtlichkeit zu verbessern), und <br>
    b) die Aktionen je nach Transaktionsanforderung auftrennen (INSERT vs. SELECT)

    Man kann auch eine SqlTransaction-Instanz über new anfordern und dann alle beteiligten SqlCommand/SqlConnection-Instanzen dieser Transaktion zuordnen. In diesem Fall könnte man bestimmte Arbeiten auch auf eine 2. Datenbankverbindung auslagern, weil eventuell eine vorherige Aktion (DataReader) die "Leitung blockiert". Der SqlDataReader ist unter ADO.NET das Gegenstück zum sogenannten <i>Firehouse-Cursor</i> von ADO (serverseitiger Forward-Only und Read-Only-Cursor). Ein aktiver DataReader <b>blockiert</b> die Datenbankverbindung, im Gegensatz zu ADO spaltet ADO.NET im Hintergrund keine neue Verbindungen ab, wenn die bestehende ausgelastet ist

    Comment


    • #3
      Hi Andreas,<br>

      also ich habe nun noch weiter "geforscht" und bin zu folgendem Ergebniss gekommen:<br>
      MS stellt mit dem DataReader ein Objekt zur Verfügung dass man NICHT in einer Transaktion benutzen kann. Es schliesst alle vorherigen Transaktionen einfach mal so ab. In der Hilfe gibt es nen kleines Sätzchen dazu, dass dies nur kurz beschreibt. Für mich vollkommen unverständlich.<br>

      <p> Zu Deinen Vorschlägen:<br>
      1. Mach ich doch: myReader = myCommand.<b>ExecuteReader</b>(System.Data.CommandBehavior.Default); <br>
      2. Mach ich auch ... myReader.Close();
      3. Nun ... meine Überlegung war, dass der Reader Daten liest, die ich dann abfragen kann. Ich habe die Hilfe so verstanden, dass ein Reader ein komplettes Abbild des SELECTS im Speicher hält, wärend der DataAdapter z.B. einen Zugriffszeiger benutzt.
      <br>
      Zu a) SqlCommand kann aber keine kompletten Recordsets übergeben. Dazu braucht man immer nen DataReader oder DataAdapter oder ....<br>
      Zu b) Das macht gar keinen Sinn. Ich brauche die Transaktion um sauber Daten lesen und schreiben und wieder lesen und wieder .... zu können - ohne das sie durch einen anderen Thread, Nutzer usw. in diesem Moment verfälscht werden. Ist ja Sinn einer Transaktion.

      <p>Danke, dass Du Dir auch nen Kopf machst. Vielleicht findet sich noch eine gute Lösung.

      <p> Christian Zin

      Comment


      • #4
        Nachtrag:<br>

        Ich reduziere mal meine Frage auf einen ganz kurzen Satz:

        Wie kann ich eine Transaktion für ADO.net erstellen, in der Datensätze eingefügt werden, andere Ausgelesen werden und dieser wieder (an anderer Stelle z.B) eingefügt werden.
        <p>
        Also einfach nur:<br>
        1. TransactionBeginn<br>
        2. Insert<br>
        3. SELECT<br>
        4. INSERT<br>
        5. TransactionCommit<br>
        <p> -> Wenns fehlschlägt nen Rollback ....<br>
        Ich habe (leider) folgende Erfahrungen gemacht: SQLDataAdapter wie auch SQLDataReader schliessen einfach mal so begonnene Transactionen ... bzw Arbeiten nicht sauber mit ihr.

        <p> Christian Zin

        Comment


        • #5
          Hallo,

          &gt;..Zu Deinen Vorschlägen:..

          das ware keine Vorschläge, sondern die Beschreibung, warum das nicht funktionieren kann.

          &gt;..dass ein Reader ein komplettes Abbild des SELECTS im Speicher hält

          Nein, ein DataReader ist nur ein serverseitiger Cursor, der bei jedem Read-Aufruf den nächsten Datensatz von der Ergebnismenge des SQL-Servers abfordert (also ein serverseitiger Cursor, der nur vorwärts laufen kann und nur Platz für einen <b>einzigen</b> Datensatz hat). Der DataReader darf erst dann geschlossen werden, wenn man seine Daten nicht mehr benötigt. Da ein DataReader aber die Verbindung blockiert, muss man ihn so früh wir nur möglich schließen.

          Nur das DataSet (genauer gesagt die DataTable-Instanzen im Dataset) können vollständige Ergebnismengen lokal speichern.

          Das folgende Microsoft-Beispiel demonstriert, dass die Transkationen in ADO.NET funktionieren:
          <pre>
          public void TransferMoney( string toAccount, string fromAccount, decimal amount )
          {
          using ( SqlConnection conn = new SqlConnection(
          "server=(local);Integrated Security=SSPI;database=SimpleBank" ) )
          {
          SqlCommand cmdCredit = new SqlCommand("Credit", conn );
          cmdCredit.CommandType = CommandType.StoredProcedure;
          cmdCredit.Parameters.Add( new SqlParameter("@AccountNo", toAccount) );
          cmdCredit.Parameters.Add( new SqlParameter("@Amount", amount ));

          SqlCommand cmdDebit = new SqlCommand("Debit", conn );
          cmdDebit.CommandType = CommandType.StoredProcedure;
          cmdDebit.Parameters.Add( new SqlParameter("@AccountNo", fromAccount) );
          cmdDebit.Parameters.Add( new SqlParameter("@Amount", amount ));

          conn.Open();
          // Start a new transaction
          using ( SqlTransaction trans = conn.BeginTransaction() )
          {
          // Associate the two command objects with the same transaction
          cmdCredit.Transaction = trans;
          cmdDebit.Transaction = trans;
          try
          {
          cmdCredit.ExecuteNonQuery();
          cmdDebit.ExecuteNonQuery();
          // Both commands (credit and debit) were successful
          trans.Commit();
          }
          catch( Exception ex )
          {
          // transaction failed
          trans.Rollback();
          // log exception details . . .
          throw ex;
          }
          }
          }
          </pre>

          &#10

          Comment

          Working...
          X