【发布时间】:2021-04-20 14:13:49
【问题描述】:
我的程序中有三个DataViews,每个都有一个DataTable 连接,我从SqlAdapter 完整和更新(通过合并和删除一些行)。更新是在异步方法中完成的,需要的地方包含在我自己的 Task.Run(...) Actions 中。更新后DataGridViews 不会显示更新的数据,尽管DataGridView、DataView 和DataTable 的组合通常会处理这一点。
这似乎破坏了绑定到DataViews 的DataGridViews 的自动刷新。观察DataGridViews 的DataBindingComplete 事件表明,当我在异步方法中更新我的底层DataTables 时,它没有被正确触发。在更新所有三个DataTables 时,我只观察到在更新过程中为DataGridViews 之一触发的事件,其中DataTable... 在异步方法完成更新后只有一行还有更多的行永远不会显示。我的DataGridViews 会在我更改列的排序后立即显示正确的更新数据。
在触发刷新的按钮事件中我做了另一个测试:
private async void AddSomethingToolStripButton_Click(object sender, EventArgs e) {
if (sender is ToolStripButton button) {
int test1 = DataGridView1.RowCount; // 4
int test2 = ((DataGridView1.DataSource as BindingSource).DataSource as DataView).Count; // 4
/* Calls async method to write new value to DB and calls more async methods to refresh
* all the DataTables with the new data which also effects the one in this example */
await controller.AddSomethingToSqlDbAndRefreshDataTableAsync();
int test3 = DataGridView1.RowCount; // Still 4
int test4 = ((DataGridView1.DataSource as BindingSource).DataSource as DataView).Count; // 5
}
}
这些值永远不会像应有的那样同步。在尝试通过在DataGridView 上调用Refresh 或在Binding 上调用ReadValues 手动刷新后,我设法通过在BindingSource 上调用ResetBindings 来使其正常工作,这感觉不太对。
我有什么遗漏吗?我不应该将这些数据结构和机制与async/await 模式结合使用吗?
编辑 - 刷新方法示例:
private async Task RefreshDataViewAsync(string query, DataView view) {
await Task.Run(() => {
using var con = new SqlConnection(sqlConnectionString);
using var adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand(query, con);
var newtable = new DataTable();
adapter.FillSchema(newtable, SchemaType.Source);
foreach (DataColumn col in newtable.Columns) {
if (ColumnValueTypeMapping.TryGetValue(col.ColumnName, out Type coltype)) {
col.DataType = coltype;
}
}
adapter.Fill(newtable);
view.Table.Merge(newtable, false);
/* Remove rows that are not present anymore */
var rows2del = new List<DataRow>();
string[] primarykeys = view.Table.PrimaryKey.Select(keycol => keycol.ColumnName).ToArray();
foreach (DataRow row in view.Table.Rows) {
if (!newtable.Rows.Contains(primarykeys.Select(key => row.Field<object>(key)).ToArray())) {
rows2del.Add(row);
}
}
rows2del.ForEach(row => row.Delete());
view.Table.AcceptChanges();
});
}
【问题讨论】:
-
如果需要访问BindingSource,存储为Field对象直接使用即可。 -- 包裹在我自己的 Task.Run(...) Actions 部分中的位置在哪里?如果你使用异步方法,
Task.Run()是干什么用的?您是否正在重置绑定的 DataTable?如果这样做,绑定就会丢失。 -
我只为我的测试访问了这样的绑定源,如果将来使用它们是我选择的解决方案,我会让它更漂亮:DI 添加了一个我从不清除的刷新方法示例/删除 DataView 或附加到它的 DataTable。使用 Task.Run 是因为没有异步的 SqlAdapter 方法版本。当我更改列的排序时,绑定仍然有效。
-
更新数据表后,DGV 不会自动重新绘制。诀窍是设置为空。 DataGridView1.DataSource = null; DataGridView1.DataSource = newtable;
-
Task.Run() 在 ThreadPool 线程中执行。你有
SqlConnection.OpenAsync(),SqlCommand.ExcuteReadAsync(),你可以await,然后[DataTable].Load([IDataReader]),所以你可以删除Task.Run()。 -- 不确定 Merge 和 DataRow 篡改的用途。在合并具有相同架构的两个表时,您不应该使用指定如何处理 PrimaryKey 的 Merge 方法重载吗?传递 DataTable 而不是 DataView,然后将其作为 DataTable 访问。更新通知必须到达BindingSource,否则不知道。 -
@Jimi 这些是删除我不知道的 Task.Run() 的方法。我可以调查一下,但我无法想象这会改变问题。行回火是删除本地副本中不再存在于远程的行。当然我可以传递表格而不是视图,但是更改事件的链仍然必须是 DataGridView->Binding->DataView->DataTable,不是吗?
标签: c# .net winforms async-await datagridview