IDbCommand缺少ExecuteReaderAsync方法

12

我正在使用.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' 操作符,将以同步方式运行。考虑使用 'await' 操作符等待非阻塞 API 调用,或者使用 'await Task.Run(...)' 在后台线程上执行 CPU 绑定工作。

我正考虑按照警告中的建议将 command.ExecuteReader() 语句转换为 await Task.Run(() => command.ExecuteReader())。但我不确定这是否是正确的方法,我认为 Task.Run(...) 是用于执行基于CPU的工作,而这主要是IO操作。

所以我的问题是

  1. Task.Run(...) 是正确的方法吗?
  2. 如果不是,有其他解决方案吗?
  3. 还是我现在应该忽略警告,并等待 ExecuteReaderAsync 添加到 IDbCommand 接口中?(计划中是否有这样的计划?)

3
"IDbCommand" 目前不太可能很快得到 "ExecuteReaderAsync" 方法——这可能会破坏没有异步/等待支持的现有实现。然而,"DbCommand" 有这个方法,而且所有 "IDbCommand" 的框架实现都继承自它,所以将其转换并没有太大的风险。 - Jeroen Mostert
我赞同JeroenMostert所说的。您可以始终检查DbCommand是否可行,如果是,则将其转换为它,以便您可以访问异步成员。 - Nkosi
你也可以考虑使用 StackExchange.Dapper - Nkosi
@Nkosi 目前我正在尝试使用现有的框架,即不引入第三方组件,除非绝对必要。目前进展顺利。不过我会看一下 Dapper。 - Kevin Brydon
你应该考虑使用 Dapper 微 ORM,因为它使得与 Ado.net 的工作变得更加简单,并且具有所有异步选项。甚至不需要担心打开/关闭连接。 - Mrinal Kamboj
1个回答

9
< p > 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并添加await即可实现。例如:
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”。如果你认为你知道类型,并且没有准备好处理错误,请直接进行硬转换。 - Jeroen Mostert
2
谢谢@YvetteColomb。我现在决定转换为DbCommand。我不太确定创建一个单独的任务会如何帮助。那不是只会将问题移动到另一个方法,我仍然会收到所有警告吗? - Kevin Brydon
2
似乎将DbCommand转换引起了一些问题。当我尝试第二次获取数据(刷新页面)时,我收到一个“已经存在与此Command相关的打开的DataReader必须首先关闭”的异常。我的连接和事务都被包裹在using中,所以不确定发生了什么。 - Kevin Brydon
2
@KevinBrydon 也尝试将命令包装起来。此外,看一下这个答案关于最后一个错误的 https://dev59.com/-mMl5IYBdhLWcg3wV10F#21131596 - Nkosi
2
@YvetteColomb 误报警。是我的代码出了问题。我有一个应该返回Task但实际上返回void的方法。在这个方法内部调用了我的一个异步仓库。吸取教训! - Kevin Brydon
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接