Iva*_*kov 6 .net c# sqlite asynchronous entity-framework-core
我有WPF程序,我试图在那里使用EF Core和SQLite,我发现了奇怪的行为.即使我调用ToArrayAsync()或SaveChangesAsync()之类的异步方法,它也会返回已完成的任务.所以这意味着操作实际上是同步完成的.
似乎EF或SQLite连接中应该有一些控制同步/异步执行的标志,但我没有找到它.
我用这个代码进行测试:
using (var context = new TestDbContext())
{
//I have about 10000 records here.
var task = context.Users.ToListAsync();
if (task.IsCompleted && task.Result != null)
{
// It is always comes here.
}
await task;
}
Run Code Online (Sandbox Code Playgroud)
这是因为 ADO.NET 类 ( DbConnection, DbCommand) 的SQLite 实现是同步的。父类提供Async真正同步的方法,提供更好的实现是提供者的工作。例如,这里是实现DbConnection.OpenAsync:
public virtual Task OpenAsync(CancellationToken cancellationToken)
{
TaskCompletionSource<object> completionSource = new TaskCompletionSource<object>();
if (cancellationToken.IsCancellationRequested)
{
completionSource.SetCanceled();
}
else
{
try
{
this.Open();
completionSource.SetResult((object) null);
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
}
return (Task) completionSource.Task;
}
Run Code Online (Sandbox Code Playgroud)
如您所见,没有任何异步内容,返回的任务始终已完成。
中的所有默认Async实现也是如此DbCommand:它们都使用TaskCompletionSource或直接使用Task.FromResult。
SQLiteCommand 不会覆盖该行为,并且当它覆盖时 - 它在对不支持异步执行的方法的注释中明确指出。例如,这里是实现(覆盖)ExecuteReaderAsync:
/// <summary>
/// Executes the <see cref="P:Microsoft.Data.Sqlite.SqliteCommand.CommandText" /> asynchronously against the database and returns a data reader.
/// </summary>
/// <param name="behavior">A description of query's results and its effect on the database.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// SQLite does not support asynchronous execution. Use write-ahead logging instead.
/// </remarks>
/// <seealso href="http://sqlite.org/wal.html">Write-Ahead Logging</seealso>
public virtual Task<SqliteDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<SqliteDataReader>(this.ExecuteReader(behavior));
}
Run Code Online (Sandbox Code Playgroud)
相比之下SqlConnection,SqlCommand类确实会覆盖默认(同步)行为并提供真正异步的方法实现,例如OpenAsyncor ExecuteReaderAsync,因此对于 sql server provider,您不应该有您观察到的行为。
因此,在使用 SQLite 时,您观察到的行为是预期的,而不是错误的。
由于您在 WPF 应用程序中使用它 - 这意味着尽管使用 async\await 您的 UI 线程将在整个操作期间被阻塞。因此,在这种情况下最好的做法是根本不使用异步版本,而是通过Task.Run或类似的构造将整个内容分派到后台线程。