【问题标题】:Odd effect of async / await on loading a datatableasync / await 对加载数据表的奇怪影响
【发布时间】:2022-01-22 01:31:00
【问题描述】:

我很难理解这段代码发生了什么。我已经做了一段时间的异步/等待,但之前没有偶然发现这个问题(除非我只是没有看到我做了什么)

我正在将 CSV 文件加载到 DataTable 中,然后我想从 DataTable 中提取列名列表以供以后操作。

在第一种方法中,我加载 CSV 异步然后分配 DataGrid 来查看数据。我无法在第一种方法中使用 _ColumnList = ... 因为无论出于何种原因,“_dtSampleCSV.Columns”都没有解析;它没有看到它是一个 DataTable。

所以我将该行移至另一个方法,在该方法中提取列名并将这些名称分配给另一个 DataGrid。当它点击此方法时,_dtSampleCSV DataTable 中没有数据(没有行,没有列),但第一个 DataGrid 确实填充了数据。

我一直认为使用“等待”会等待线程完成后再继续。所以要么这不是真的,要么线程确实完成了,但尚未分配 DataTable。

我只能假设此时数据不存在,但 DataGrid 的源已设置为在填充数据时;表明。但是,如果 await 没有完成将数据加载到 DataTable 中,我该如何防止执行?

private DataTable _dtSampleCSV = new DataTable();
private List<string> _ColumnList = new List<string>();

private async void btnAdd_Click(object sender, RoutedEventArgs e)
{
    spNew.Visibility = Visibility.Visible;

    OpenFileDialog openFileDialog = new OpenFileDialog();
    if (openFileDialog.ShowDialog() == true)
    {
        tbFilePath.Text = openFileDialog.FileName;
        var _dtSampleCSV = await LoadCSVAsync(openFileDialog.FileName);
            
        dgCSVExample.DataContext = _dtSampleCSV;
        dgCSVExample.Visibility = Visibility.Visible;

        SetColumnGrid();

    }
}

private void SetColumnGrid()
{
    _ColumnList = (from DataColumn dc in _dtSampleCSV.Columns.Cast<DataColumn>() select dc.ColumnName).ToList();
    dgColumnNames.DataContext = _ColumnList;  // breakpoint set here to look at _dtSampleCSV and no data exists at runtime
    dgColumnNames.Visibility = Visibility.Visible;
}

// csv method
public static async Task<DataTable> LoadCSVAsync(string filePath)
{
    using (var reader = File.OpenText(filePath))
    {
        var fileText = await reader.ReadToEndAsync();
        using (TextReader sr = new StringReader(fileText))
        {
            var adapter = new GenericParsing.GenericParserAdapter(sr);
            adapter.FirstRowHasHeader = true;
            adapter.MaxBufferSize = 4096;
            adapter.MaxRows = 3;
            DataTable dtProcess = adapter.GetDataTable();
            return dtProcess;
        }
    }
}

【问题讨论】:

  • 您已经在 btnAdd_Click 方法 - var _dtSampleCSV = ... 中声明了一个局部变量。因此,您实际上从未为SetColumnGrid 中使用的字段 分配新值。只需删除 var 部分。

标签: c# async-await


【解决方案1】:

您的LoadCSVAsync 方法是static,这就解释了为什么它无法访问_dtSampleCSV 实例字段。

您的btnAdd_Click 方法创建了一个名为_dtSampleCSV 的局部变量,将其分配给dgCSVExample.DataContext,然后将其丢弃。来自该局部变量的数据在 _dtSampleCSV 字段中将不可用。

更改您的方法,以便将数据分配给字段,而不是创建局部变量:

// Remove this:
// var _dtSampleCSV = await LoadCSVAsync(openFileDialog.FileName);

// Use this:
_dtSampleCSV = await LoadCSVAsync(openFileDialog.FileName);

注意:您应该尽可能尝试avoid async void methods

【讨论】:

  • 哦。答对了!这就是我在凌晨 3 点进行编码并返回的结果。我看不到森林里的树木。谢谢;就是这样!
  • "你应该尽量避免使用异步 void 方法。" -- 不是当 async void 用于它的 intended purpose 时,它专门用于制作异步事件处理程序可能。
  • 顺便说一句,我在 async/await 上使用 void 的唯一地方是在按钮等事件处理程序上。我是根据几年前读到的几个地方做的,包括这个:pluralsight.com/guides/returning-void-from-c-async-method
  • @TheodorZoulias 来自那篇文章:“我喜欢采用的一种方法是最小化我的异步事件处理程序中的代码——例如,让它等待包含实际逻辑的异步任务方法。”
  • Richard 引用的文字是关于重构代码以提高可测试性。 async void 事件处理程序仍然存在。
猜你喜欢
  • 2019-10-05
  • 2021-01-20
  • 1970-01-01
  • 2014-04-08
  • 1970-01-01
  • 1970-01-01
  • 2017-12-11
  • 2018-07-26
  • 1970-01-01
相关资源
最近更新 更多