web-dev-qa-db-fra.com

ReadOnlyException DataTable DataRow "La colonne X est en lecture seule."

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:

  • ExampleQueryOld

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;
}
  • ExampleQueryNew

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?

39
jp2code

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; 
81
Fun Mun Pieng

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";

1
Carlos A Merighe

En VB, ne transmettez pas un élément DataRow en lecture seule par référence

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 

Sortie

Column 'Name' is read only

C-sharp

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. 

0
Walter Stabosz