【问题标题】:IDbCommand missing ExecuteReaderAsyncIDbCommand 缺少 ExecuteReaderAsync
【发布时间】:2017-08-23 19:17:19
【问题描述】:

我正在使用 .NET Core 2.0。我有以下函数调用IDbCommand.ExecuteReader

public async Task<IEnumerable<Widget>> ReadAllAsync(
    System.Data.IDbConnection databaseConnection,
    System.Data.IDbTransaction databaseTransaction)
{
    var commandText = "SELECT WidgetId, Name FROM Widget";

    // _databaseCommandFactory.Create returns an IDbCommand
    var command = this._databaseCommandFactory.Create(databaseConnection, databaseTransaction, commandText);

    using (var dataReader = command.ExecuteReader())
    {
        // iterate through the data reader converting a collection of Widgets (`IEnumerable<Widget>`)
    }
}

我收到警告

此异步方法缺少“等待”运算符,将同步运行。考虑使用 'await' 运算符来等待非阻塞 API 调用,或使用 'await Task.Run(...)' 在后台线程上执行 CPU 密集型工作。

我正在考虑按照警告中的建议将 command.ExecuteReader() 语句转换为 await Task.Run(() =&gt; command.ExecuteReader())。但我不确定这是正确的方法,我相信Task.Run(...) 是用于基于 CPU 的工作。这主要是 IO 工作。

所以我的问题是

  1. Task.Run(...) 是正确的方法吗?
  2. 如果没有,是否有其他解决方案?
  3. 或者我应该暂时忽略警告并等到ExecuteReaderAsync 被添加到IDbCommand 接口? (有这方面的计划吗?)

【问题讨论】:

  • IDbCommand 不太可能很快得到ExecuteReaderAsync——它可能会破坏不支持异步/等待的现有实现。但是DbCommand 确实有它,并且IDbCommand 的所有框架实现都继承自它,因此转换它的风险不大。
  • 我赞同 JeroenMostert 所说的。您始终可以检查 DbCommand,如果可行,将其强制转换,以便您可以访问异步成员。
  • 你也可以考虑使用StackExchange.Dapper
  • @Nkosi 我目前正在查看我可以按原样使用框架做些什么,即除非绝对必要,否则不引入第三方组件。到目前为止进展顺利。不过我会看看 Dapper。
  • 您应该考虑使用 Dapper Micro ORM,因为它使使用 Ado.net 变得更加简单,并且提供了所有 Async 选项。甚至不必担心打开/关闭连接。

标签: c# asynchronous async-await .net-core-2.0


【解决方案1】:

await 关键字允许方法异步运行。 async 关键字允许在方法中使用 await 关键字并协助管理返回。

在调用await 之前,该方法将同步运行。

所以所有这些都是同步运行的。在完成之前,它不会返回任何内容或在方法中移动。

public async Task<IEnumerable<Widget>> ReadAllAsync(
    System.Data.IDbConnection databaseConnection,
    System.Data.IDbTransaction databaseTransaction)
{
    var commandText = "SELECT WidgetId, Name FROM Widget";

    // _databaseCommandFactory.Create returns an IDbCommand
    var command = this._databaseCommandFactory.Create(databaseConnection, databaseTransaction, commandText);

    using (var dataReader = command.ExecuteReader())
    {
        // iterate through the data reader converting a collection of Widgets (`IEnumerable<Widget>`)
    }
}

通过转换为DbCommand,大多数IDbCommand 派生实现已经这样做,然后转换为DbCommand 并添加等待将起作用,例如

var dbCommand = (DbCommand) command;
using (var dataReader = await dbCommand.ExecuteReaderAsync())
{
    while (await dataReader.ReadAsync()) 
    {
        // iterate through the data reader converting a collection of Widgets (`IEnumerable<Widget>`)
    }
}

或创建一个单独的任务

public async Task MyAsyncMethod()
{
  // Do your stuff that takes a long time
}

public async Task CallMyAsyncMethod()
{
  // We can await Tasks, regardless of where they come from.
  await MyAsyncMethod();

}

这样 - 程序将在等待从该方法返回的同时继续运行,而不是锁定 UI 和其他所有内容。

【讨论】:

  • (DbCommand) command 有幸立即使用InvalidCastException 失败。使用as 获取对象,然后使用它,只会让您看到一个神秘的NullReferenceException。如果你认为你知道类型是什么,并且你还没有准备好处理错误,那就硬转换吧。
  • 谢谢@YvetteColomb。我现在决定使用 DbCommand。我不完全确定创建单独的任务会有什么帮助。这不只是将问题转移到不同的方法,我仍然会收到所有警告吗?
  • 看起来强制转换为 DbCommand 导致了一些问题。我收到“已经有一个打开的 DataReader 与此命令关联,必须先关闭它。”当我第二次尝试获取数据(刷新页面)时出现异常。我的连接和事务都包含在 usings 中,所以不确定发生了什么。
  • @KevinBrydon 也尝试包装命令。也看看这个答案关于最后一个错误stackoverflow.com/a/21131596/5233410
  • @YvetteColomb 误报。我的代码有问题。有一个本应返回 Task 但返回 void 的方法。该方法内部是对我的一个异步存储库的调用。吸取教训!
猜你喜欢
  • 2012-03-15
  • 1970-01-01
  • 1970-01-01
  • 2012-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
相关资源
最近更新 更多