ReadOnlyException 数据表(DataTable)数据行(DataRow)"X列是只读的。"

53

我有一小段代码,最初多次创建了一个 SqlDataAdapter 对象。

为了使调用更加简洁,我将 SqlDataAdapter 替换为 SqlCommand,并将 SqlConnection 移到循环外部。

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

注意:我有一个自定义函数,根据员工的ID获取他们的全名。这里为了简单起见,我在下面的示例代码中使用了"John Doe"来演示我的观点。

ExampleQueryOld 使用 SqlDataAdapter;每当我尝试写入 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在幕后做了一些事情?


1
这里有一些用户似乎不喜欢我。 - user153923
1
可能是 DataTable.Load(IReader) 方法将列设置为只读。你可以将所有列的 ReadOnly 属性设置为 false 吗? - Mike Park
1
也许吧!如果我知道在哪里找到这样的“ReadOnly”属性的话。 :) - user153923
1
由于 DataTableDataRowDataRow item 没有 ReadOnly 属性,我尝试使用 DataTable.Load(IReader, LoadOption.Upsert)DataTable.Load(IReader, LoadOption.Upsert)(默认值)和 DataTable.Load(IReader, LoadOption.OverwriteChanges) 修改了 DataTable.Load(IReader) 命令,但结果都是一样的:ReadOnlyException - user153923
14
在这段代码中,使用foreach循环遍历tab对象的所有列(DataColumn),并将每一列的ReadOnly属性设置为false。 - k3b
4个回答

105
使用 DataAdapter.Fill 方法不会加载数据库架构信息,例如列是否为主键、是否只读等。要加载数据库架构信息,请使用 DataAdapter.FillSchema 方法,但这不是你的问题。
使用 DataReader 填充表可以加载架构信息。因此,index 列是只读的(可能是因为它是主键),并且该信息被加载到 DataTable中,从而防止您修改表中的数据。
我认为 @k3b 是正确的;通过设置 ReadOnly = false,您应该能够向数据表中写入数据。
foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false; 

彭先生:一年后的跟进。在 DataReader 检索数据后,是否有一种方法可以卸载丢弃模式? - user153923
2
@jp2code 你可能可以创建一个新表,仅复制数据而不包括模式信息。如果只是想更改特定的属性,比如只读标志,最好直接设置该属性的值。 - Fun Mun Pieng
我今天已经用完了我的点赞次数,但是对这个答案给予虚拟的+1。在发现其中一列是只读的之前,我为此苦战了几个小时。现在我的小DataGridView像魔法一样工作得很好! :) 谢谢! - Misiu

3

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


1

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

你遇到这个问题的可能性很低,但我正在处理一些VB.NET代码时遇到了ReadOnlyException

我遇到这个问题是因为代码将DataRow Item通过ByRef传递给一个Sub。仅仅是通过引用传递就会触发异常。

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 

输出

列“名称”是只读的

C#

在C#中,您甚至无法编写此类代码。 DoNothing(ref dt.Rows[0].Item["Name"])会导致编译时错误。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接