Comportamiento de bloqueo con los métodos de Entity Framework Async y SQL Server Compact

Tengo una aplicación MVVM que llama a un servicio de datos para obtener algunos datos para vincular. El servicio de datos accede a una base de datos SQL Server Compact (v4.0) a través de Entity Framework 6.

Los datos (actualmente) tardan unos segundos en cargarse y, cuando se los llama sincrónicamente, esto (como es lógico) bloquea el hilo de la GUI. La mayor parte de esto suponía que estaba vinculado a IO, así que agregué un método Async equivalente para ejecutar la carga de datos de manera asíncrona para permitir que la GUI permanezca receptiva. Sin embargo, cuando uso los métodos EF Async, no parece hacer ninguna diferencia, el hilo de la GUI todavía se bloquea, aproximadamente por la misma cantidad de tiempo.

Entiendo que el uso de las llamadas asíncronas no moverá el trabajo a un hilo diferente, sin embargo, asumí que la mayoría de esta actividad está vinculada a IO. Por lo tanto, el uso de los métodos EF Async debería permitir que el hilo de llamada (GUI) continúe haciendo otro trabajo, pero no hace ninguna diferencia.

Por ejemplo, si escribo el método LoadData para usar el método de carga síncrona EF, la GUI se bloquea hasta que se cargan los datos:

public ObservableCollection<Data> GetData()
{
    //Do IO bound activity...
    Context.DataTable1.Include(...).Load();

    //for demo purposes, just return some data
    return Context.DataTable1.Local; //(ObservableCollection<Data>)
}

El método asíncrono para cargar los datos se ve así:

public async Task<ObservableCollection<Data>> GetDataAsync()
{
    //Do IO bound activity...
    var task = Context.DataTable1.Include(...).LoadAsync();
    await task;

    //for demo purposes, just return some data
    return Context.DataTable1.Local; //(ObservableCollection<Data>)
}

Sorprendentemente (para mí) obtengo el mismo resultado y bloquea el hilo de llamada durante aproximadamente el mismo período de tiempo (le puse un cronómetro).

Comencé a pensar que, además del trabajo vinculado a la base de datos IO, puede haber una cantidad mínima de actividad vinculada a la CPU que está causando el bloqueo. Así que finalmente intenté ejecutar el trabajo en un hilo de fondo usando Task.Run ():

public async Task<ObservableCollection<Data>> GetDataAsync()
{
    //Do IO bound activity...
    Task<ObservableCollection<Competition>> task = Task.Run(async () => 
    { 
        //Do IO bound activity...
        var task = Context.DataTable1.Include(...).LoadAsync();
        await task;

        //for demo purposes, just return some data
        return Context.DataTable1.Local; //(ObservableCollection<Data>)
    });
    var result = await task;
    return result;
}

Con esto, la GUI obviamente no se bloquea y responde todo el tiempo.

He leído muchos artículos sobre todo esto, incluidas las publicacionesaquí yaquí y publicaciones de blog de Stephen Cleary sobre cuándo no (aquí) y cuándo (aquí) use Task.Run. Entiendo que mi último ejemplo anterior todavía está bloqueando, solo está bloqueando un hilo de grupo de subprocesos. Lo que no entiendo realmente es, ¿por qué al acceder a los métodos asíncronos de EF no parece proporcionar algún beneficio?

¿Podría ser que mientras hay actividad IO en curso, también hay suficiente trabajo vinculado a la CPU para causar el bloqueo? Ciertamente, se tarda mucho más de 50 ms en responder, más cerca de 2 o 3 segundos para ejecutar todas las consultas, por lo que este tipo de actividad puede comenzar a justificar el uso de la Tarea.

O, ¿he escrito el método Async incorrectamente, por lo tanto, ¿por qué sigue bloqueándose?

También me preguntaba si quizás los métodos asíncronos para el proveedor de SQL Compact para EF estaban por alguna razón recurriendo a las llamadas síncronas estándar.

Respuestas a la pregunta(1)

Su respuesta a la pregunta