BindingSource 看到变化,DataTable 没有
我最近从 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);
});
Johannes:
对 EndEdit() 的调用是保存处理程序中的第二行(靠近我的帖子顶部)。我也应该在其他地方调用它吗?
谢谢。
只是为了澄清:SaveVehicles 不可能是问题的根源,因为问题是在调用 SaveVehicles 之前出现的。在调用 EndEdit() 之后和实际写入数据库之前,什么情况可能会导致 BindingSource 和 DataTable 之间出现差异?
I have recently switched over from Java/RMI to C# / .net, and am working on my first project using databinding to update records in Oracle. On this first form I'm building, I have a detail view for vehicle records (VIN, year/make/model, license plate number, that sort of thing). The first thing I did in terms of writing to the DB was saving updates:
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.");
}
}
}
That all seemed ok, so I moved on to adding new records. I made a button (I saw online where various people had said it was as good or better to make your own than use a binding navigator) and put this in its handler:
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;
So there (above) I'm putting initial values into some required columns (VEH_ID is the PK). Then I ran the app, entered a value in the textbox for VIN, and went to save (same code as above) and this time GetChanges() returned null.
So I tried this in the "add new" button handler, in place of the first thing:
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;
Now I have something really interesting happening.
- I can successfully enter and save data on any number of new records, until I select an existing record. If I move to an existing record and then add a new record, then when I go to save the new record, the values that were explicitly set in code get written to the DB but data entered into the GUI do not "take" for the new record.
- I can successfully change any number of existing records, until I enter a new record. If I add one or more new records, save, and then try to save changes to an existing record, the call to GetChanges() returns null (so again, apparently it is not "seeing" what's been entered through the GUI).
So in both of these cases, the change from old to new, or new to old, appears to introduce some condition that makes the datatable unaware of what was entered into the GUI. But in changing from old to new it only takes selecting an existing record, whereas with changing from new to old, it only breaks after saving (if I do a new record but then abandon it without saving, I can modify existing records without problems).
I added this into the save handler, just prior to the actual save (in a loop because ch is a datatable, but really the code is set up to where you have to either save or abandon the new record before moving on - thus the loop only executes once):
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 + "'");
}
Where VIN_ID
is the column to which this particular textbox is bound (I tried this with other fields on the form too, to verify it wasn't just something flaky about that one textbox). The first message box (DataRowView from the binding source) shows the vin that I entered into the textbox. The second message box (row from the table returned by GetChanges()) shows the empty string for VIN_ID
, although it has the correct (set through code) values for VEH_ID
and GRNTE_ID
. The same thing happens if I select a different value for GRNTE_ID
using the combo box bound to that column; the row from the datatable still has the value that was set through code, "unaware" of the value selected through the GUI.
Why would the datatable and binding source have different values for the same field? And why would the datatable be able to "see" values entered through the GUI only until the user switches from existing to new, or from new to existing?
Thanks.
Angel:
I'm doing that in my Service:
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;
}
This is called from this block from my original post:
Service<TVDDataService.IService1>.Use(svcProxy =>
{
isSaved = svcProxy.SaveVehicles(ch);
});
Johannes:
The call to EndEdit() is the second line in the save handler (near the top of my post). Should I be calling it somewhere else as well?
Thanks.
Just to clarify: SaveVehicles cannot be the source of the problem, since the problem is appearing before SaveVehicles is ever called. What condition could cause a discrepancy between the BindingSource and the DataTable after EndEdit() has been called and before anything actually writes to the DB?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用 EndEdit() 后必须更新表适配器;您也可以使用以下代码片段更新完整的数据集:
希望有所帮助...
You have to update your table adapter after use EndEdit(); also you can update your complete DataSet with the follow snippet :
Hopes Helps...
*如果您也使用 BindingSource:
只需执行一个简单的 BindingSource.EndEdit(),您的 TextBox 数据就会发送到 DataTable。
示例:-
希望这可以帮助任何在这里遇到困难的人。
*IF you are using a BindingSource as well:
Just do a simple BindingSource.EndEdit() and your TextBox data will be sent over to the DataTable.
Example:-
Hope this helps anyone who stumbles here.