问题描述
我有 WPF 程序,我试图在那里使用 EF Core 和 SQLite,但我发现了奇怪的行为.即使我调用 ToArrayAsync() 或 SaveChangesAsync() 等异步方法,它也会返回已完成的任务.所以这意味着操作实际上是同步完成的.
I have WPF program and I am trying to use EF Core with SQLite there and I found strange behaviour. Even if I call async method like ToArrayAsync() or SaveChangesAsync() it returns already completed task. So it means that operation was actually done synchronously.
似乎在 EF 或 SQLite 连接中应该有一些标志来控制同步/异步执行,但我没有找到.
It seems that there should be some flag in EF or SQLite connection which control sync/async execution but I didn't find it.
我使用此代码进行测试:
I used this code for tests:
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;
}
推荐答案
这是因为 ADO.NET 类(DbConnection
、DbCommand
)的 SQLite 实现是同步的.父类提供真正同步的 Async
方法,提供更好的实现是提供者的工作.例如,这里是 DbConnection.OpenAsync
的实现:
That's because SQLite implementations of ADO.NET classes (DbConnection
, DbCommand
) are synchronous. Parent classes provide Async
methods that are really synchronous, and it's a job of provider to provide better implementation. For example, here is implementatation of 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;
}
如你所见,没有什么异步的东西,返回的任务总是完成.
As you see, there is nothing asynchronous whatsover, and returned task is always completed.
同样适用于 DbCommand
中的所有默认 Async
实现:它们都使用 TaskCompletionSource
或直接使用 Task.FromResult代码>.
The same goes for all default Async
implementations in DbCommand
: they all either use TaskCompletionSource
or directly Task.FromResult
.
SQLiteCommand 不会覆盖该行为,并且当它覆盖时 - 它在对方法的注释中明确指出不支持异步执行.例如,这里是 ExecuteReaderAsync
的实现(覆盖):
SQLiteCommand does not override that behavior, and when it does - it says explicitly in comments to the methods that asynchronous execution is not supported. For example, here is implementation (overriden) of 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));
}
相比之下 - SqlConnection
和 SqlCommand
类确实会覆盖默认(同步)行为,并提供诸如 OpenAsync
或 等方法的真正异步实现>ExecuteReaderAsync
,所以对于 sql server provider,你不应该有你观察到的行为.
By contrast - SqlConnection
and SqlCommand
classes do override default (synchornous) behavior and provide really asynchronous implementations of methods like OpenAsync
or ExecuteReaderAsync
, so with sql server provider you should not have the behavior you observe.
因此,在使用 SQLite 时,您观察到的行为是预期的,而不是错误的.
So the behavior you observe is expected and not buggy when using SQLite.
由于您在 WPF 应用程序中使用它 - 这意味着尽管使用了 async\await,您的 UI 线程将在整个操作期间被阻塞.因此,在这种情况下最好不要使用异步版本,而是通过 Task.Run
或类似结构将整个内容分派到后台线程.
Since you are using this in WPF application - that would mean that despite using async\await you UI thread will be blocked for the duration of the whole opration. So best thing to do in this case is not not use async versions at all and dispatch whole thing to the background thread via Task.Run
or similar construct.
这篇关于实体框架核心 + SQlite.异步请求实际上是同步的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!