Oracle 托管驱动程序可以正确使用 async/await 吗?

Posted

技术标签:

【中文标题】Oracle 托管驱动程序可以正确使用 async/await 吗?【英文标题】:Can the Oracle managed driver use async/await properly? 【发布时间】:2015-05-15 00:26:08 【问题描述】:

我试图使用 async/await .NET 功能进行 Oracle 查询。结果集非常大,大约需要 5-10 秒才能返回。 Window_Loaded 正在挂起 UI 线程,本质上我想使用 async/wait 在后台进行查询,然后用结果更新数据视图。

那么这是 Oracle 驱动程序问题还是代码错误?例如。这里的事情是同步而不是异步完成的吗?我正在使用最新的Oracle.ManagedDataAccess,我可以从 Oracle 的网站获得。

async Task<DataTable> AccessOracleAsync()

    DataTable dt;
    using(OracleConnection conn = new OracleConnection(ConfigurationManager
        .ConnectionStrings["connStr"].ConnectionString))
    using (OracleCommand cmd = new OracleCommand(@"SELECT * FROM myTbl", conn))
    
        await conn.OpenAsync();
        using (var reader = await cmd.ExecuteReaderAsync())
        
            dt = new DataTable();
            dt.Load(reader);                        
        
    
    return dt;


private async void Window_Loaded(object sender, RoutedEventArgs e)

    await AccessOracleAsync();


我试过了,它仍然阻塞 UI:

async Task<DataView> AccessOracleAsync()

    DataTable dt;
    using (OracleConnection conn = new OracleConnection(ConfigurationManager
        .ConnectionStrings["connStr"].ConnectionString))
    using (OracleCommand cmd = new OracleCommand(@"SELECT * FROM myTbl", conn))
    
        await conn.OpenAsync().ConfigureAwait(false);
        using (DbDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
        
            dt = new DataTable();
            await Task.Run(() => dt.Load(reader)).ConfigureAwait(false);
        
    
    return dt.AsDataView();


private async void Window_Loaded(object sender, RoutedEventArgs e)

    Data1.ItemsSource = await AccessOracleAsync();


所以最后,我把方法改成这样,让它不会阻塞。看来我的想法是对的,只是 Oracle 托管库同步实现了 Async 方法(仅符合接口)。

private async Task<DataView> AccessOracleAsync()

    DataTable dt = new DataTable();
    using (OracleConnection conn = new OracleConnection(ConfigurationManager
        .ConnectionStrings["connStr"].ConnectionString))
    using (OracleCommand cmd = new OracleCommand(@"SELECT * myTbl", conn))
    
        await Task.Run(() =>
        
            conn.Open();
            using (DbDataReader reader = cmd.ExecuteReader())
            
                dt.Load(reader);
            
        ).ConfigureAwait(false);
    
    return dt.AsDataView();

【问题讨论】:

您可以尝试获取代码 dt.Load(reader);进入一个任务,比如 await Task.Run(() => dt.Load(reader));此外,代码对我来说看起来不错。 仅供参考,我向 Oracle 发送了关于异步支持的电子邮件并收到了回复:“我没有任何关于 ODP.NET 托管驱动程序何时需要异步的新信息。” 他们在 7 天前发布了新版本 18.3(最后一个是 12.2)。 nuget.org/packages/Oracle.ManagedDataAccess/18.3.0 也许这个新的真的是异步的。谁能确认一下? @lmcarreiro 当然不是。甲骨文没有计划实施这一点。解决这个问题的唯一方法是转移到……嗯,几乎可以说是任何其他数据库,因为其他数据库似乎没有 Oracle 的问题。 我编辑了问题中的文字,将“死锁”替换为“块”,我认为更准确。死锁是永远阻塞的东西,而不仅仅是几秒钟。 【参考方案1】:

(我将此作为答案,因为它似乎是让 Oracle 托管驱动程序正确支持异步的“解决方案”。)

我在 Oracle 的网站上找到了 old thread(从 2010 年开始),Oracle PM 说他们不支持它。您可以vote(必须有 Oracle 帐户)来包含该功能。遗憾的是,5 年后它只获得了 60 票。

【讨论】:

现在是 2019 年,一切都没有改变。 来自 Oracle .NET 团队:“还没有异步支持。这也是我们正在开展的一个项目。”我们仍然有希望使用 Oracle。 twitter.com/OracleDOTNET/status/1227374014177280000 @DoubleJ 他们说他们也会在 5 年前研究它。你真的信任甲骨文吗? 近 6 年过去了,我们仍在等待。 gg 甲骨文。 现在是 2021 年,一切都没有改变。尚无异步支持。【参考方案2】:

没有。托管驱动程序不支持async / await

您可以调用这些方法,因为它们必须按照接口定义来实现,但代码实际上是同步的。如果你愿意,你可以使用Task.Run,但你不能同时有两个调用(Oracle 会威胁它们同步)。

【讨论】:

谢谢,在尝试获取async/await 时,某些标记为async 的方法实际上是同步实现的,这非常令人困惑。 @user17753 不仅令人困惑,it's frustrating。 微软没有实现驱动。甲骨文可以。 @tacos_tacos_tacos 虽然情况并非如此,但 Oracle 甚至可能没有意识到他们应该重写从 DbConnectionDbCommand 继承的异步方法的实现......这个实现只是依赖于同步方法。 ODAC 存在于 ADO.Net 类型上的那些异步方法之前。它们被添加到 ADO.Net 类型上而不太可能是抽象的,因为否则这对所有提供者来说都是一个重大变化。至于在默认实现引发异常或同步回退之间进行选择,我个人更喜欢后者。这是微软的选择。 @Frédéric 他们确实知道,因为几年前我与他们负责 .NET 驱动程序的人交谈过。我宁愿他们抛出 NotImplementedException 而不是错误地让你相信他们有异步支持。

以上是关于Oracle 托管驱动程序可以正确使用 async/await 吗?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle官方非托管Odac驱动与Oracle官方托管odac驱动

教你正确打开async/await关键字的使用

OwinCommunicationListener 中托管的 Async Web Api 中的 Fire and Forget 方法

如何正确理解.NET 4.5和C#5.0中的async/await异步编程模式

如何从使用 Futures/async/await 的流中正确生成?

正确使用带有 Await/Async 的 Promise