System.Data.IDbCommand和异步执行?

时间:2012-02-02 11:49:25

标签: c# sql interface asynchronous sqlclient

System.Data.SqlClient.SqlCommand 有方法

BeginExecuteNonQuery
BeginExecuteReader
BeginExecuteXmlReader

EndExecuteNonQuery
EndExecuteReader
EndExecuteXmlReader

用于异步执行。

System.Data.IDbCommand 只有

ExecuteNonQuery
ExecuteReader
ExecuteXmlReader

仅用于同步操作。

是否有任何异步操作接口?
另外,为什么没有BeginExecuteScalar?

8 个答案:

答案 0 :(得分:7)

我建议在使用数据库API时将DbCommand及其朋友视为接口。为了在各种数据库提供者上推广API,DbCommand实现与IDbCommand一样好 - 或者可以说更好,因为它包含更新的技术,例如正确await能{{} 1}} Task成员。

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

另一方面,MS可以在不破坏ABI的情况下向DbCommand添加新的虚拟方法。向基类添加新方法可能会被视为破坏API(编译时,并不像运行时那样中断),因为如果您继承了DbCommand并添加了一个具有相同名称的成员,那么您将开始获得警告CS0108: 'member1' hides inherited member 'member2'. Use the new keyword if hiding was intended.)。因此,DbCommand可以获得新功能,对遵循良好实践的代码消耗影响最小(例如,大多数东西将继续工作,只要它不对类型系统起作用并使用{{{{{{ 1}})。

MS可能用来支持喜欢接口的人的一种可能的策略是引入名为DbCommand的新接口,并myCommand.GetType().GetMethods()[3].Invoke(myCommand, …)实现它们。他们没有这样做。我不知道为什么,但他们可能没有这样做,因为它会增加复杂性,直接消费IAsyncDbCommand的替代方案为消费接口提供了大部分好处,但缺点很少。也就是说,它的回报很少。

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

为了解决这个问题,我构建了一个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<string>(0);
                            Assert.IsNotNull(name);
                        }
                    }
                } while (await reader.NextResultAsync());
            }
        }

答案 3 :(得分:2)

IDbCommand没有开始/结束异步方法,因为它们在ADO.NET的原始.NET 1.1版本中尚不存在,而当异步方法为added in .NET 2.0时,它本来就是将更改添加到IDbCommand的重大更改(向接口添加成员是对该接口的实现者的重大更改)。

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

当我需要将数据调用迁移到异步方法时,我偶然发现了这个问题。我为incorporate async interface创建了未来.NET Standard的问题。与此同时,我还创建了a library with a set of interfaces and adapters for System.Data

答案 7 :(得分:-8)

没有它们的接口

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