51

我有一小段代码,它最初一遍又一遍地创建了一个SqlDataAdapter对象。

为了稍微简化我的调用,我将SqlDataAdapter替换为SqlCommand并将SqlConnection移到循环之外。

现在,每当我尝试编辑返回到我的DataTable的数据行时,我都会抛出一个以前没有抛出的ReadOnlyException 。

注意:我有一个自定义函数,可以根据他们的 ID 检索员工的全名。为了简单起见,我在下面的示例代码中使用了“John Doe”来证明我的观点。

ExampleQueryOldSqlDataAdapter一起使用;每当我尝试写入DataRow的元素时, ExampleQueryNew 都会失败并出现ReadOnlyException

  • 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在幕后做某事?

4

4 回答 4

99

usingDataAdapter.Fill不加载数据库模式,包括列是否为主键,以及列是否为只读。要加载数据库架构,请使用DataAdapter.FillSchema,但这不是您的问题。

使用DataReader来填充表格会加载架构。因此,该index列是只读的(可能是因为它是主键)并且该信息被加载到DataTable. 从而防止您修改表中的数据。

我认为@k3b 是对的;通过设置ReadOnly = false,您应该能够写入数据表。

foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false; 
于 2011-03-27T05:51:45.260 回答
2

在尝试不同的方法时,我一直遇到同样的异常。最后对我有用的是将列的 ReadOnly 属性设置为 false 并更改Expression列的值而不是 row[index] = "new value";

于 2016-10-17T17:20:06.700 回答
0

在 VB 中,不要通过引用传递只读 DataRow 项

您遇到这种情况的可能性很低,但我正在编写一些 VB.NET 代码并获得了ReadOnlyException.

我遇到了这个问题,因为代码将 DataRow 项传递给 Sub ByRef。只是传递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

升C

你甚至不能在 C# 中编写这样的代码。DoNothing(ref dt.Rows[0].Item["Name"])给你一个编译时错误。

于 2018-11-29T18:32:53.060 回答
-1

打开数据集的 yourdataset.xsd 文件。单击表或对象,然后单击需要更改只读属性的特定列。其简单的解决方案。

于 2014-08-30T15:04:19.790 回答