我有一小段代码,最初反复创建 SqlDataAdapter 对象。
尝试简化我的调用,我将 SqlDataAdapter 替换为 SqlCommand ,并将 SqlConnection 移到循环之外。< / p>
现在,每当我尝试编辑返回到 DataTable 的数据行时,我都会抛出之前未抛出的 ReadOnlyException 。
注意:我有一个自定义功能,可根据其ID检索员工的全名。为简单起见,我在下面的示例代码中使用了“John Doe”来证明我的观点。
ExampleQueryOld 适用于 SqlDataAdapter ;每当我尝试写入 DataRow 的元素时, ExampleQueryNew 就会失败并显示 ReadOnlyException :
这有效,没有问题:
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;
}
此示例抛出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 在幕后做某事?
答案 0 :(得分:83)
使用DataAdapter.Fill
不会加载数据库架构,其中包括列是否为主键,以及列是否为只读。要加载数据库架构,请使用DataAdapter.FillSchema
,但这不是您的问题。
使用DataReader
填充表格加载架构。因此,index
列是只读的(可能是因为它是主键),并且该信息被加载到DataTable
中。从而阻止您修改表中的数据。
我认为@ k3b做对了;通过设置ReadOnly = false
,您应该能够写入数据表。
foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false;
答案 1 :(得分:1)
在尝试不同的方法时,我一直遇到同样的异常。最终对我有用的是将列的ReadOnly属性设置为false并更改 Expression 列的值而不是row [index] =“new value”;
答案 2 :(得分:0)
您遇到这种情况的可能性很小,但是我正在研究某些VB.NET代码并获得了ReadOnlyException
。
我遇到了这个问题,因为代码将DataRow项传递给Sub ByRef。只是pass-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#中编写这样的代码。 DoNothing(ref dt.Rows[0].Item["Name"])
给您一个编译时错误。
答案 3 :(得分:-1)
打开数据集的yourdataset.xsd文件。单击表或对象,然后单击需要更改readonly属性的特定列。 简单的解决方案。