WPF 프로그램이 있고 거기에 SQLite와 함께 EF Core를 사용하려고하는데 이상한 동작을 발견했습니다. ToArray 메서드 (예 : 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;
}
이는 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;
}
보시다시피, 비동기는 아니며 반환 된 작업은 항상 완료됩니다.
DbCommand
모든 기본 Async
구현에 Task.FromResult
입니다. 모두 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));
}
대조적으로 - SqlConnection
및 SqlCommand
클래스는 기본 (동기화) 동작을 재정의하고 OpenAsync
또는 ExecuteReaderAsync
와 같은 메서드를 실제로 비동기로 구현하므로 SQL Server 공급자의 경우에는 관찰하지 않아야합니다.
그래서 당신이 관찰하는 행동은 예상되며 SQLite를 사용할 때 버그가 없습니다.
WPF 응용 프로그램에서 이것을 사용하고 있기 때문에 async \ await를 사용함에도 불구하고 UI 스레드가 전체 opration 동안 차단됩니다. 이 경우 가장 좋은 방법은 비동기 버전을 전혀 사용하지 않고 Task.Run
또는 유사한 구조를 통해 백그라운드 스레드로 모든 것을 전송하는 것입니다.