我最近从 Java/RMI 切换到 C#/.net,并且正在开发我的第一个项目,使用数据绑定来更新 Oracle 中的记录。在我正在构建的第一个表格中,我有一个车辆记录的详细视图(VIN、年份/品牌/型号、车牌号等)。在写入数据库方面,我做的第一件事是保存更新:
private void btn_saveDesc_Click(object sender, EventArgs e)
{
bool isSaved = false;
hJA_VEHICLEBindingSource.EndEdit();
DataSet1.HJA_VEHICLEDataTable ch =
(DataSet1.HJA_VEHICLEDataTable)dataSet1.HJA_VEHICLE.GetChanges();
if (ch == null)
{
MessageBox.Show("There are no changes to save.");
}
else
{
Service<TVDDataService.IService1>.Use(svcProxy =>
{
isSaved = svcProxy.SaveVehicles(ch);
});
if (isSaved)
{
// update the vehicle in the local dataset
var modifiedRows = from row in dataSet1.HJA_VEHICLE
where row.RowState == DataRowState.Modified
select row;
foreach (DataRow row in modifiedRows)
{
row.Delete();
}
dataSet1.HJA_VEHICLE.Merge(ch);
dataSet1.HJA_VEHICLE.AcceptChanges();
}
if(isSaved)
{
MessageBox.Show("The record has been saved.");
}
else
{
MessageBox.Show("The record could not be saved.");
}
}
}
一切看起来都不错,所以我继续添加新记录。我做了一个按钮(我在网上看到很多人都说自己制作比使用绑定导航器更好或更好)并将其放入它的处理程序中:
DataRowView drv = (DataRowView)hJA_VEHICLEBindingSource.AddNew();
currVeh_id = nextID(); // generate arbitrary ID for the record
drv["VEH_ID"] = currVeh_id;
drv["GRNTE_ID"] = lastSelectedGranteeID; // just to have an initial value
hJA_VEHICLEBindingSource.Filter = "VEH_ID = " + currVeh_id;
所以在那里(上面)我将初始值放入一些必需的列中(VEH_ID 是 PK)。然后我运行应用程序,在文本框中为 VIN 输入一个值,然后保存(与上面的代码相同),这次 GetChanges() 返回 null。
所以我在“添加新”按钮处理程序中尝试了这个,而不是第一件事:
DataSet1.HJA_VEHICLERow newRow =
(DataSet1.HJA_VEHICLERow)dataSet1.HJA_VEHICLE.NewRow();
currVeh_id = nextID();
newRow.VEH_ID = currVeh_id;
newRow.GRNTE_ID = lastSelectedGranteeID;
dataSet1.HJA_VEHICLE.AddHJA_VEHICLERow(newRow);
hJA_VEHICLEBindingSource.Filter = "VEH_ID = " + currVeh_id;
现在我发生了一些非常有趣的事情。
- 在选择现有记录之前,我可以成功输入并保存任意数量的新记录的数据。如果我移动到现有记录,然后添加新记录,那么当我去保存新记录时,代码中明确设置的值会写入数据库,但输入到 GUI 中的数据不会“占用”新纪录。
- 我可以成功更改任意数量的现有记录,直到输入新记录。如果我添加一个或多个新记录,保存,然后尝试将更改保存到现有记录,则对 GetChanges() 的调用返回 null(同样,显然它不是“看到”通过 GUI 输入的内容)。
所以在这两种情况下,从旧到新或从新到旧的变化似乎会引入一些条件,使数据表不知道输入到 GUI 中的内容。但是从旧到新只需选择现有记录,而从新到旧更改时,它只会在保存后中断(如果我做了一个新记录然后放弃它而不保存,我可以毫无问题地修改现有记录) .
我将它添加到保存处理程序中,就在实际保存之前(在循环中,因为 ch 是一个数据表,但实际上代码设置为在继续之前您必须保存或放弃新记录的位置 - 因此循环只执行一次):
foreach (DataSet1.HJA_VEHICLERow r in ch)
{
DataRowView drv = (DataRowView)hJA_VEHICLEBindingSource.Current;
MessageBox.Show(drv["VIN_ID"].ToString());
MessageBox.Show(r.VEH_ID + "\n" + r.GRNTE_ID + "\n'"
+ r.VIN_ID + "'");
}
这个特定文本框绑定到的列在哪里VIN_ID
(我也在表单上的其他字段中尝试过这个,以验证它不仅仅是那个文本框的问题)。第一个消息框(来自绑定源的 DataRowView)显示了我在文本框中输入的 vin。第二个消息框(GetChanges() 返回的表中的行)显示 的空字符串VIN_ID
,尽管它具有正确的(通过代码设置)值VEH_ID
和GRNTE_ID
。GRNTE_ID
如果我选择不同的值来使用绑定到该列的组合框,也会发生同样的事情;数据表中的行仍然具有通过代码设置的值,“不知道”通过 GUI 选择的值。
为什么数据表和绑定源对同一字段有不同的值?为什么数据表只能“看到”通过 GUI 输入的值,直到用户从现有切换到新的,或从新的切换到现有的?
谢谢。
天使:我在我的服务中这样做:
public bool SaveVehicles(DataSet1.HJA_VEHICLEDataTable vehicles)
{
bool saved = false;
if (vehicles != null && !vehicles.HasErrors)
{
HJA_VEHICLETableAdapter ta = new HJA_VEHICLETableAdapter();
int result = ta.Update(vehicles);
saved = (result > 0);
}
return saved;
}
这是从我原来的帖子中的这个块调用的:
Service<TVDDataService.IService1>.Use(svcProxy =>
{
isSaved = svcProxy.SaveVehicles(ch);
});
约翰内斯:
对 EndEdit() 的调用是保存处理程序中的第二行(靠近我的帖子的顶部)。我也应该在其他地方调用它吗?
谢谢。
澄清一下:SaveVehicles 不可能是问题的根源,因为问题出现在 SaveVehicles 被调用之前。在调用 EndEdit()之后和实际写入数据库之前,什么条件会导致 BindingSource 和 DataTable 之间的差异?