System.Data.IDbCommand和异步执行?

System.Data.SqlClient.SqlCommand有方法

BeginExecuteNonQuery BeginExecuteReader BeginExecuteXmlReader 

 EndExecuteNonQuery EndExecuteReader EndExecuteXmlReader 

用于异步执行。

System.Data.IDbCommand只有

 ExecuteNonQuery ExecuteReader ExecuteXmlReader 

这些仅用于同步操作。

是否存在异步操作的接口?
另外,为什么没有BeginExecuteScalar?

IDbCommand没有开始/结束异步方法,因为它们在ADO.NET的原始.NET 1.1版本中尚不存在,并且当在.NET 2.0中添加异步方法时,将这些方法添加到IDbCommand (向接口添加成员是该接口的实现者的重大变化)。

我不知道为什么BeginExecuteScalar不存在,但它可以作为包装BeginExecuteReader的扩展方法实现。 无论如何,在.NET 4.5中,我们现在拥有更容易使用的ExecuteScalarAsync

我建议在使用数据库API时将DbCommand及其朋友视为接口。 为了在各种数据库提供程序上推广API, DbCommand实现与IDbCommand一样好 – 或者更好,因为它包括更新的技术,例如正确await能够成为Task *Async()成员。

MS无法向IDbCommand添加任何具有新function的新方法。 如果他们要向IDbCommand添加一个方法,那么这是一个重大改变,因为任何人都可以在他们的代码中自由地实现该接口,并且MS在框架中花了很多精力来保持ABI和API兼容性。 如果他们在.net版本中扩展接口,之前工作的客户代码将停止编译,并且未重新编译的现有程序集将开始遇到运行时错误。 另外,他们无法通过扩展方法添加正确的*Async()Begin*()方法,而不会在幕后对DbCommand进行丑陋的DbCommand (这本身就是一种不好的做法,破坏了类型安全性并且不必要地引入了动态运行时强制转换)。

另一方面,MS可以在不破坏ABI的情况下向DbCommand添加新的虚拟方法。 向基类添加新方法可能会被视为破坏API(编译时,并不像运行时那样中断)因为如果inheritance了DbCommand并添加了具有相同名称的成员,您将开始收到警告CS0108: ‘member1’隐藏了inheritance成员’member2’。 如果要隐藏,请使用new关键字。 )。 因此, DbCommand可以获得新function,对遵循良好实践的代码消耗影响最小(例如,只要不对类型系统起作用并且使用类似myCommand.GetType().GetMethods()[3].Invoke(myCommand, …)调用方法,大多数东西将继续工作myCommand.GetType().GetMethods()[3].Invoke(myCommand, …) )。

MS可能用于支持喜欢接口的人的可能策略是引入具有IAsyncDbCommand等名称的新接口,并让DbCommand实现它们。 他们没有这样做。 我不知道为什么,但他们可能没有这样做,因为它会增加复杂性,而直接使用DbCommand的替代方案为消耗具有少量缺点的接口提供了大部分好处。 也就是说,这将是很少回报的工作。

实际上,创建等同于BeginExecuteNonQuery,EndExecuteNonQuery等的异步行为将是相当困难的任务。 这些API的实现远远优于简单生成单独的线程,等待数据库响应和调用回调。 它们依赖于I / O重叠并提供更好的线程经济性。 在网络跳跃,命令的数据库处理期间不会消耗额外的线程 – 这可能是呼叫花费的总时间的99%。 对于几个调用它没有任何区别,但是当您设计高吞吐量服务器时,线程经济性变得非常重要。

我想知道为什么缺少BeginExecuteScalar。 此外,大多数其他提供商(例如ODP.Net)根本没有异步API!

是的,没有异步操作的接口。

为了解决这个问题,我构建了一个shim,如果它们存在于IDbConnection.IDbCommand / IDataReader上,则调用异步方法,如果不存在,则调用常规方法。

资料来源: https : //github.com/ttrider/IDbConnection-Async

NuGet: https : //www.nuget.org/packages/IDbConnection-Async/

例:

  using (IDbConnection connection = new SqlConnection(connectionString)) { await connection.OpenAsync(); IDbCommand command = connection.CreateCommand(); command.CommandText = "SELECT Name FROM Person;"; using (IDataReader reader = await command.ExecuteReaderAsync()) { do { while (await reader.ReadAsync()) { if (!await reader.IsDBNullAsync(0)) { var name = reader.GetFieldValueAsync(0); Assert.IsNotNull(name); } } } while (await reader.NextResultAsync()); } } 

即使您正在检索“一个值”,大部分时间都将用于1)网络跳转到数据库服务器,2)数据库服务器执行命令。 比你花费更多的时间来读取数据集中的1000条记录。 所以,我同意,目前尚不清楚为什么没有BeginExecuteScalar ……

您可以通过自定义代码实现异步行为,因为它不是那么复杂,就像您的问题一样 – 您的目标没有任何标准的异步操作。

当我需要将数据调用迁移到异步方法时,我偶然发现了这个问题。 我为未来的.NET Standard创建了一个包含异步接口的问题 。 与此同时,我还为System.Data创建了一个带有一组接口和适配器的库 。

没有它们没有接口

没有BeginExecuteScalar的原因是因为您可能不需要异步调用来获取单个值,这应该非常快