System.Data.IDbCommand 和异步执行?

发布于 2025-01-01 16:13:41 字数 400 浏览 6 评论 0原文

系统.Data.SqlClient.SqlCommand 有方法

BeginExecuteNonQuery
BeginExecuteReader
BeginExecuteXmlReader

EndExecuteNonQuery
EndExecuteReader
EndExecuteXmlReader

用于异步执行。

系统.数据.IDb命令 仅

ExecuteNonQuery
ExecuteReader
ExecuteXmlReader

适用于同步操作。

有异步操作的接口吗?
另外,为什么没有 BeginExecuteScalar ?

System.Data.SqlClient.SqlCommand
has methods

BeginExecuteNonQuery
BeginExecuteReader
BeginExecuteXmlReader

and

EndExecuteNonQuery
EndExecuteReader
EndExecuteXmlReader

for asynchronous execution.

System.Data.IDbCommand
only has

ExecuteNonQuery
ExecuteReader
ExecuteXmlReader

which are for synchronous operations only.

Is there any interface for asynchronous operations ?
In addition, why is there no BeginExecuteScalar ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

苏别ゝ 2025-01-08 16:13:41

我建议在使用数据库 API 时将 DbCommand 及其朋友视为接口。为了在各种数据库提供程序上通用 API,DbCommand 的实现与 IDbCommand 一样好,或者可以说更好,因为它包含更新的技术,例如适当的 awaitable Task *Async() 成员。

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

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

MS 可以用来支持喜欢界面的人们的一个可能策略是引入名为 IAsyncDbCommand 的新界面,并让 DbCommand 实现它们。他们没有这样做。我不知道为什么,但他们可能没有这样做,因为这会增加复杂性,而直接使用 DbCommand 的替代方案提供了使用接口的大部分好处,而且几乎没有缺点。也就是说,这将是一项回报甚微的工作。

I recommend to treat DbCommand and its friends as if they were interfaces when consuming database APIs. For the sake of generalizing an API over various database providers, DbCommand achieves just as well as IDbCommand—or, arguably, better, because it includes newer technologies such as proper awaitable Task *Async() members.

MS can’t add any new methods with new functionality to IDbCommand. If they were to add a method to IDbCommand, it is a breaking change because anyone is free to implement that interface in their code and MS has put much effort into preserving ABI and API compatibility in the framework. If they expanded interfaces in a release of .net, customer code which previously worked would stop compiling and existing assemblies which are not recompiled would start encountering runtime errors. Additionally, they can’t add proper *Async() or Begin*() methods via extension methods without doing ugly casting to DbCommand behind the scenes (which is a bad practice itself, breaking type safety and unnecessarily introducing dynamic runtime casting).

On the other hand, MS can add new virtual methods to DbCommand without breaking ABI. Adding new methods to a base class might be considered breaking the API (compile-time, not as bad to break as runtime) because if you inherited DbCommand and had added a member with the same name, you’ll start getting the warning CS0108: 'member1' hides inherited member 'member2'. Use the new keyword if hiding was intended.). Thus, DbCommand can get the new features with minimal impact on consuming code which follows good practices (e.g., most stuff will keep working as long as it doesn’t work against the type system and call methods using something like myCommand.GetType().GetMethods()[3].Invoke(myCommand, …)).

A possible strategy which MS could have used to support people who like interfaces would have been to introduce new interfaces with names like IAsyncDbCommand and have DbCommand implement them. They haven’t done this. I don’t know why, but they probably didn’t do this because it would increase complication and the alternative of directly consuming DbCommand provides most of the benefits to consuming interfaces with few downsides. I.e., it would be work with little return.

寻梦旅人 2025-01-08 16:13:41

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

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

IDbCommand does not have the begin/end async methods because they did not yet exist in the original .NET 1.1 release of ADO.NET, and when the async methods were added in .NET 2.0 it would have been a breaking change to add those to IDbCommand (adding members to an interface is a breaking change for implementors of that interface).

I don't know why BeginExecuteScalar doesn't exist, but it can be implemented as an extension method that wraps around BeginExecuteReader. Anyway in .NET 4.5 we now have ExecuteScalarAsync which is easier to use.

蹲墙角沉默 2025-01-08 16:13:41

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

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

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

Actually, creating async behavior equivalent to BeginExecuteNonQuery, EndExecuteNonQuery, etc. would be rather difficult task. Implementation of these APIs are far superior to simple spawning a separate thread, waiting for the database response and invoking callback. They rely on the I/O overlapping and provide much better thread economy. No additional threads are consumed for the duration of the network hop, database processing of the command - which is probably 99% of the overall time spent on the call. For a couple of calls it makes no difference, but when you designing a high throughput server, thread economy becomes very important.

I was wondering myself why BeginExecuteScalar is missing. Also, most of other providers, including ODP.Net for example, have no async API at all!

And yes, there is no interface for async operations.

年少掌心 2025-01-08 16:13:41

为了准确解决这个问题,我构建了一个填充程序,如果 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());
            }
        }

To solve exactly this problem I built a shim that calls async methods if they exist on IDbConnection.IDbCommand/IDataReader or call regular methods if they don't.

Source:
https://github.com/ttrider/IDbConnection-Async

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

Example:

        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());
            }
        }
掩耳倾听 2025-01-08 16:13:41

即使您正在检索“一个值”,大多数时间也会花费在 1) 到数据库服务器的网络跃点,2) 数据库服务器执行命令上。比您将 1000 条记录读入数据集所花费的时间要多得多。所以,我同意,目前还不清楚为什么没有 BeginExecuteScalar...

Even if your are retrieving "one value" most of the time will be spent on 1) network hop to the database server, 2) database server executing command. Much more time than you will spend on say reading 1000 records into data set. So, I agree, it's not clear why there is no BeginExecuteScalar...

夏末的微笑 2025-01-08 16:13:41

当我需要将数据调用迁移到异步方法时,我偶然发现了这个问题。我为未来的 .NET 标准创建了一个问题,以合并异步接口。同时,我还创建了一个包含一组系统接口和适配器的库.数据

I stumbled upon this question when I need to migrate my data calls to async methods. I've created an issue for future .NET Standard to incorporate async interface. In the mean time, I've also created a library with a set of interfaces and adapters for System.Data.

情绪少女 2025-01-08 16:13:41

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

You may implement async behavior by your custom code, since it is not so complicated, as for your question - there is no any standard async operations for your goals.

謸气贵蔟 2025-01-08 16:13:41

不,它们没有接口

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

No there are not interfaces for them

The reason why there is not a BeginExecuteScalar is because you probably won't need an async call to get one single value back which should be very quick

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文