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:
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;
}
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?
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;
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.
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
Column 'Name' is read only
Sie können nicht einmal Code wie diesen in C # schreiben. DoNothing(ref dt.Rows[0].Item["Name"])
gibt einen Fehler beim Kompilieren aus.