在C#中使用SQLite和async

kyvafyod  于 2023-04-12  发布在  SQLite
关注(0)|答案(3)|浏览(302)

我正在努力了解async/await关键字和用法,我想我已经掌握了基本知识。但是在我的SQLite代码中有些东西不太好用。
我正在一个简单的项目中使用SQLite.core NuGet package,我注意到我写的异步代码没有异步运行(如我所料),所以我创建了一个更简单的测试项目来测试我的理解。
在我的测试代码中,我打开了一个到内存中数据库的连接(对于基于文件的数据库,我也遇到了同样的问题。在测试代码中,内存中更容易),并使用ExecuteNonQueryAsync发出了一个“create table”命令。我没有立即使用await来获得结果,而是在最后使用await关键字之前向控制台写入了一些内容。
我希望控制台命令在ExecuteNonQueryAsync完成之前执行,所以在我的测试中,我应该看到“1 2 3 4”。
我使用SQL Server LocalDB连接运行了一个相同的测试(运行相同的代码,只有DbConnection不同),并得到了预期的“1 2 3 4”。
我错过了什么?我是否需要在SQLite中使用特殊的连接字符串来支持async方法?它是否支持它?
我的完整测试项目可以在here中找到。
下面是主程序本身:

namespace DatabaseTest
   {
    using System;
    using System.Data.Common;
    using System.Data.SqlClient;
    using System.Data.SQLite;
    using System.Threading.Tasks;
class Program
{
    static void Main(string[] args)
    {
        Task.WaitAll(TestDatabase(true), TestDatabase(false));
    }

    private static async Task TestDatabase(bool sqLite)
    {
        Console.WriteLine("Testing database, sqLite: {0}", sqLite);
        using (var connection = CreateConnection(sqLite))
        {
            connection.Open();
            var task = ExecuteNonQueryAsync(connection);
            Console.WriteLine("2");
            await task;
            Console.WriteLine("4");
        }
    }

    private static DbConnection CreateConnection(bool sqLite)
    {
        return sqLite ?
            (DbConnection)new SQLiteConnection(string.Format("Data Source=:memory:;")) :
            new SqlConnection(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\DatabaseTest.mdf;Integrated Security=True;Connect Timeout=30");
    }

    private static async Task ExecuteNonQueryAsync(DbConnection connection)
    {
        var command = connection.CreateCommand();
        command.CommandText = "CREATE TABLE test (col1 integer);";
        Console.WriteLine("1");
        await command.ExecuteNonQueryAsync();
        Console.WriteLine("3");
    }
}

和输出:

Testing database, sqLite: True
1
3
2
4
Testing database, sqLite: False
1
2
3
4
mmvthczy

mmvthczy1#

System.Data.SQLite的实现是100%同步的。它们没有任何异步重载,这是微软造成的误解,因为SQLiteCommand扩展了System.Data.Common.DbCommand,默认实现了 *Async方法,只调用同步版本:

/// <summary>This is the asynchronous version of <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" />. Providers should override with an appropriate implementation. The cancellation token may optionally be ignored.The default implementation invokes the synchronous <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" /> method and returns a completed task, blocking the calling thread. The default implementation will return a cancelled task if passed an already cancelled cancellation token.  Exceptions thrown by <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" /> will be communicated via the returned Task Exception property.Do not invoke other methods and properties of the <see langword="DbCommand" /> object until the returned Task is complete.</summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <exception cref="T:System.Data.Common.DbException">An error occurred while executing the command text.</exception>
public virtual Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
{
  ...
  return Task.FromResult<int>(this.ExecuteNonQuery());
  ...
}

我也是这么想的,我对他们的方法不满意,但这就是我们所得到的。为了记录在案,我认为应该有NotSupportedException

sy5wg1nm

sy5wg1nm2#

一旦你启动了一个异步任务,这个任务和主线程都可以继续运行,所以不能保证哪一个运行得更快。
SQLite是一个没有客户端/服务器通信开销的嵌入式数据库,并且作为一个库,在同一个CPU上运行。因此,这个实现可能已经决定实际支持异步执行没有意义。

cuxqih21

cuxqih213#

几乎所有的ADO .NET数据库提供程序实现都是同步的(异步-〉内部路由到同步实现),除了MS SQL Server的实现是完全异步的!
System.Data.SQLite .NET数据提供程序也是同步的,因为您可以使用Write Ahead Logginghttps://www.sqlite.org/wal.html),这也是这里建议的。始终使用事务块来最大限度地减少线程阻塞(大大提高速度)!
AFAIK同步.NET数据提供程序:

  • 所有SQLite提供程序
  • Sybase(SQL Anywhere)
  • PostgreSQL
  • Oracle MySQL数据提供程序

异步的有:

  • MS SQL Server数据提供程序位于System.Data中
  • 用于MySQL的MySqlConnector
  • DevArt商业提供者,但他们没有TAP(任务异步模式)

也许可以编写自己的扩展并将该方法放入Task.Run(()=〉Func())中。
提示:当你想检查你的“.NET数据提供程序代码”是否在主进程线程之外的其他线程中运行时,在Visual Studio或VS代码的调试菜单中显示线程窗口
问候

相关问题