Подтвердить что ты не робот

ReadOnlyException DataTable DataRow "Столбец X доступен только для чтения".

У меня есть короткий фрагмент кода, который изначально создавал объект SqlDataAdapter снова и снова.

Попытка немного упростить мои вызовы, я заменил SqlDataAdapter на SqlCommand и переместил SqlConnection вне цикла.

Теперь, всякий раз, когда я пытаюсь редактировать строки данных, возвращенные в мой DataTable, я получаю бросок ReadOnlyException, который не был брошен раньше.

ПРИМЕЧАНИЕ. У меня есть настраиваемая функция, которая извлекает полное имя сотрудника на основе их идентификатора. Для простоты здесь я использовал "John Doe" в моем примере кода ниже, чтобы продемонстрировать свою точку зрения.

ExampleQueryOld работает с SqlDataAdapter; ExampleQueryNew терпит неудачу с ReadOnlyException, когда я пытаюсь записать элемент DataRow:

  • ExampleQueryOld

Это работает и не имеет проблем:

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

В этом примере генерируется исключение 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;
}

Почему я могу написать элемент DataRow в одном случае, но не в другом?

Это потому, что SqlConnection все еще открыт или SqlDataAdapter делает что-то за сценой?

4b9b3361

Ответ 1

с помощью DataAdapter.Fill не загружается схема базы данных, которая включает в себя, является ли столбец первичным ключом или нет, и является ли столбец только для чтения или нет. Чтобы загрузить схему базы данных, используйте DataAdapter.FillSchema, но тогда это не ваши вопросы.

используя DataReader, чтобы заполнить таблицу, загружает схему. Таким образом, столбец index доступен только для чтения (возможно, потому, что это первичный ключ) и эта информация загружается в DataTable. Таким образом, вы не можете изменять данные в таблице.

Я думаю, что @k3b понял это правильно; установив ReadOnly = false, вы сможете записать в таблицу данных.

foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false; 

Ответ 2

Я продолжал получать одно и то же исключение при разных подходах. Наконец, для меня было установлено, что для свойства столбца ReadOnly значение false и изменить значение столбца Expression вместо строки [index] = "new value";

Ответ 3

В VB не передавайте элемент DataRow только для чтения по ссылке

Вероятность того, что вы столкнетесь с этим, мала, но я работал над некоторым кодом VB.NET и получил ReadOnlyException.

Я столкнулся с этой проблемой, потому что код передавал элемент DataRow в Sub ByRef. Просто акт мимохода вызывает исключение.

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

С-острым

Вы даже не можете написать такой код на С#. DoNothing(ref dt.Rows[0].Item["Name"]) выдает ошибку времени компиляции.

Ответ 4

откройте файл yourdataset.xsd вашего набора данных. щелкните по таблице или объекту и щелкните по определенному столбцу, для которого необходимо изменить свойство readonly. его простые решения.