J'ai un petit morceau de code qui a créé à l'origine un objet SqlDataAdapter encore et encore.
Essayant de rationaliser un peu mes appels, j'ai remplacé le SqlDataAdapter par un SqlCommand et déplacé le SqlConnection en dehors de la boucle.
Maintenant, chaque fois que j'essaie d'éditer des lignes de données renvoyées à mon DataTable, je reçois une ReadOnlyException levée qui n'a pas été levée auparavant.
REMARQUE: J'ai une fonction personnalisée qui récupère le nom complet de l'employé en fonction de son ID. Pour plus de simplicité, j’ai utilisé "John Doe" dans mon exemple de code ci-dessous pour illustrer mon propos.
ExampleQueryOld fonctionne avec le SqlDataAdapter; ExampleQueryNew échoue avec la ReadOnlyException à chaque fois que j'essaie d'écrire dans un élément de la DataRow:
Cela fonctionne et n'a pas de problèmes:
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;
}
Cet exemple lève l'exception ReadOnlyException:
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;
}
Pourquoi puis-je écrire sur l'élément DataRow dans un cas, mais pas dans l'autre?
Est-ce parce que le SqlConnection _ est toujours ouvert ou bien le SqlDataAdapter est-il en train de faire quelque chose derrière la scène?
l'utilisation de DataAdapter.Fill
ne charge pas le schéma de base de données, qui indique si une colonne est une clé primaire ou non, et si une colonne est en lecture seule ou non. Pour charger le schéma de base de données, utilisez DataAdapter.FillSchema
, mais ce ne sont pas vos questions.
utiliser DataReader
pour remplir une table charge le schéma. Ainsi, la colonne index
est en lecture seule (probablement parce que c'est la clé primaire) et cette information est chargée dans la DataTable
. Vous empêchant ainsi de modifier les données de la table.
Je pense que @ k3b a eu raison; en définissant ReadOnly = false
, vous devriez pouvoir écrire dans la table de données.
foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false;
J'ai continué à avoir la même exception tout en essayant des approches différentes. Ce qui a finalement fonctionné pour moi a été de définir la propriété ReadOnly de la colonne sur false et de modifier la valeur de la colonne Expression au lieu de la ligne [index] = "nouvelle valeur";
La probabilité que vous rencontriez ceci est faible, mais je travaillais sur du code VB.NET et j'ai la ReadOnlyException
.
J'ai rencontré ce problème car le code transmettait l'élément DataRow à un sous-fichier ByRef. Le simple fait de passer-byref déclenche l'exception.
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
Vous ne pouvez même pas écrire un code comme celui-ci en C #. DoNothing(ref dt.Rows[0].Item["Name"])
vous donne une erreur de compilation.