web-dev-qa-db-de.com

ReadOnlyException DataTable DataRow "Spalte X ist schreibgeschützt."

Ich habe ein kurzes Stück Code erhalten, das ursprünglich ein SqlDataAdapter -Objekt immer und immer erstellt hat.

Beim Versuch, meine Anrufe ein wenig zu straffen, ersetzte ich den SqlDataAdapter durch einen SqlCommand und verlegte die SqlConnection außerhalb der Schleife.

Immer wenn ich versuche, Datenzeilen zu bearbeiten, die in meine DataTable zurückgegeben wurden, bekomme ich eine ReadOnlyException geworfen, die nicht zuvor geworfen wurde.

HINWEIS: Ich habe eine benutzerdefinierte Funktion, die den vollständigen Namen des Mitarbeiters anhand seiner ID abruft. Der Einfachheit halber habe ich in meinem folgenden Beispielcode "John Doe" verwendet, um meinen Standpunkt zu demonstrieren.

ExampleQueryOld arbeitet mit dem SqlDataAdapter ; ExampleQueryNew schlägt mit der ReadOnlyException fehl, wenn ich versuche, in ein Element der DataRow zu schreiben:

  • ExampleQueryOld

Das funktioniert und hat keine Probleme:

public static DataTable ExampleQueryOld(string targetItem, string[] sqlQueryStrings) {
  DataTable bigTable = new DataTable();
  for (int i = 0; i < sqlQueryStrings.Length; i++) {
    string sqlText = sqlQueryStrings[i];
    DataTable data = new DataTable(targetItem);
    using (SqlDataAdapter da = new SqlDataAdapter(sqlText, Global.Data.Connection)) {
      try {
        da.Fill(data);
      } catch (Exception err) {
        Global.LogError(_CODEFILE, err);
      }
    }
    int rowCount = data.Rows.Count;
    if (0 < rowCount) {
      int index = data.Columns.IndexOf(GSTR.Employee);
      for (int j = 0; j < rowCount; j++) {
        DataRow row = data.Rows[j];
        row[index] = "John Doe"; // This Version Works
      }
      bigTable.Merge(data);
    }
  }
  return bigTable;
}
  • ExampleQueryNew

In diesem Beispiel wird die ReadOnlyException ausgelöst:

public static DataTable ExampleQueryNew(string targetItem, string[] sqlQueryStrings) {
  DataTable bigTable = new DataTable();
  using (SqlConnection conn = Global.Data.Connection) {
    for (int i = 0; i < sqlQueryStrings.Length; i++) {
      string sqlText = sqlQueryStrings[i];
      using (SqlCommand cmd = new SqlCommand(sqlText, conn)) {
        DataTable data = new DataTable(targetItem);
        try {
          if (cmd.Connection.State == ConnectionState.Closed) {
            cmd.Connection.Open();
          }
          using (SqlDataReader reader = cmd.ExecuteReader()) {
            data.Load(reader);
          }
        } catch (Exception err) {
          Global.LogError(_CODEFILE, err);
        } finally {
          if ((cmd.Connection.State & ConnectionState.Open) != 0) {
            cmd.Connection.Close();
          }
        }
        int rowCount = data.Rows.Count;
        if (0 < rowCount) {
          int index = data.Columns.IndexOf(GSTR.Employee);
          for (int j = 0; j < rowCount; j++) {
            DataRow row = data.Rows[j];
            try {
              // ReadOnlyException thrown below: "Column 'index'  is read only."
              row[index] = "John Doe";
            } catch (ReadOnlyException roErr) {
              Console.WriteLine(roErr.Message);
            }
          }
          bigTable.Merge(data);
        }
      }
    }
  }
  return bigTable;
}

Warum kann ich in einem Fall auf das Element DataRow schreiben, im anderen Fall jedoch nicht?

Liegt es daran, dass die SqlConnection noch geöffnet ist oder der SqlDataAdapter etwas hinter den Kulissen tut?

39
jp2code

bei Verwendung von DataAdapter.Fill wird das Datenbankschema nicht geladen. Dazu gehört, ob eine Spalte ein Primärschlüssel ist oder nicht und ob eine Spalte schreibgeschützt ist oder nicht. Um das Datenbankschema zu laden, verwenden Sie DataAdapter.FillSchema, aber das sind nicht Ihre Fragen.

wenn Sie DataReader zum Füllen einer Tabelle verwenden, wird das Schema geladen. Daher ist die Spalte index schreibgeschützt (wahrscheinlich weil es der Primärschlüssel ist) und diese Informationen werden in die Variable DataTable geladen. Dadurch wird verhindert, dass Sie die Daten in der Tabelle ändern.

Ich denke, @k3b hat es richtig gemacht; Durch die Einstellung von ReadOnly = false sollten Sie in die Datentabelle schreiben können.

foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false; 
81
Fun Mun Pieng

Ich bekam immer wieder die gleiche Ausnahme, während ich verschiedene Ansätze ausprobierte. Was schließlich für mich funktioniert hat, war, die ReadOnly-Eigenschaft der Spalte auf false zu setzen und den Wert der Expression -Spalte anstelle von row [index] = "new value" zu ändern.

1

Übergeben Sie in VB kein schreibgeschütztes DataRow-Element als Referenz

Die Wahrscheinlichkeit, dass Sie darauf stoßen, ist gering, aber ich habe an VB.NET-Code gearbeitet und die ReadOnlyException erhalten.

Ich bin auf dieses Problem gestoßen, weil der Code das DataRow-Element an einen Sub-ByRef übergeben hat. Allein der Vorgang des Passierens von Bytes löst die Ausnahme aus.

Sub Main()

    Dim dt As New DataTable()
    dt.Columns.Add(New DataColumn With {
        .ReadOnly = True,
        .ColumnName = "Name",
        .DataType = GetType(Integer)
    })

    dt.Rows.Add(4)

    Try
        DoNothing(dt.Rows(0).Item("Name"))
        Console.WriteLine("All good")
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    End Try 

End Sub

Sub DoNothing(ByRef item As Object) 
End Sub 

Ausgabe

Column 'Name' is read only

Cis

Sie können nicht einmal Code wie diesen in C # schreiben. DoNothing(ref dt.Rows[0].Item["Name"]) gibt einen Fehler beim Kompilieren aus. 

0
Walter Stabosz