4

I have a simple Winforms application with a button on it. Using EF 6.1.1 code first, if I use .ToListAsync on a query it will freeze the form until the result came back from the SQL Server.

  private async void button1_Click(object sender, EventArgs e)
  {
      using( var context = new MyEFContext() )
      {
         var result = await context.MyTable.ToListAsync();
         MessageBox.Show(result.Count);
      }
  }

If I put the .ToListAsync() call in a different synchronization context say by adding await Task.Delay(1).ConfigureAwaiter(false) before it, it works as it should be.

Does anybody know what I'm missing here? Why is it like this?

mrtaikandi
  • 6,753
  • 16
  • 62
  • 93
  • Are you sure there aren't multiple operations going on which might hang the UI? If the button click yields control back to the message loop, might there be something else going on? – Yuval Itzchakov Dec 20 '14 at 10:01
  • The above code is exactly what it is in my demo app. – mrtaikandi Dec 20 '14 at 10:03
  • 1
    How many rows are there? – usr Dec 20 '14 at 15:41
  • Normally, there's only one synchronization context. What `.ConfigureAwaiter(false)` does is not capturing it, thus, scheduling the continuation on the thread pool instead o the synchronization context. – Paulo Morgado Dec 20 '14 at 16:59
  • If you hit the button a 2nd time does it lock up then too? – Scott Chamberlain Dec 20 '14 at 17:30
  • @usr This is a sample app, so I've just added 3 rows in the database. – mrtaikandi Dec 20 '14 at 17:38
  • 1
    @ScottChamberlain the second time is fast but it might just because there aren't a lot of rows in the database. Shouldn't be the proxy generation happens async too? How can I make sure that the second time is async and not just very fast? – mrtaikandi Dec 20 '14 at 17:46
  • If you are not planning to do updates to the returned data [disable proxy generation](http://stackoverflow.com/questions/7111109/should-i-enable-or-disable-dynamic-proxies-with-entity-framework-4-1-and-mvc3) and see if that helps. – Scott Chamberlain Dec 20 '14 at 18:08
  • @ScottChamberlain It seems that proxy generation has nothing to do with it. – mrtaikandi Dec 20 '14 at 18:34

2 Answers2

2

Basically, all async code is synchronous until the first await.

What might be happening is the setup (opening connection and such) taking too many time.

You should always push as much as you can outside of the UI thread. Something like this:

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;

private async void button1_Click(object sender, EventArgs e)
{
    this.button1.Enabled = false;
    
    var result = await GetMyTableAsync();
    
    MessageBox.Show(result.Count);
    
    this.button1.Enabled = true;
}

private async Task<IList<MyTableEntity>> GetMyTableAsync()
{
    using( var context = new MyEFContext() )
    {
        return await context.MyTable.ToListAsync()
             .ConfigureAwait(false);
    }
}
Andrei Krasutski
  • 4,913
  • 1
  • 29
  • 35
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
-1

Querying a database is an IO bound operation, so by design, it run under the same synchronisation context as the calling code.

The typical intent usage is

var task = context.Table.ToListAsync();
// do something else here 
var result = await task;

However, if you have nothing else to do then it is better to spawn a thread

var result = await Task.Run<List<Table>>(() => context.Table.ToListAsync());

which mean that your task is now both cpu and io bound

Servy
  • 202,030
  • 26
  • 332
  • 449
Tien Dinh
  • 1,037
  • 7
  • 12
  • 1
    An async IO bound operation shouldn't block the UI. You shouldn't need to spin a new thread for that. – Yuval Itzchakov Dec 20 '14 at 09:56
  • It shouldn't block the UI thread. From the MSDN documentations: In client applications (WinForms, WPF, etc.) the current thread can be used to keep the UI responsive while the async operation is performed - http://msdn.microsoft.com/en-us/data/jj819165.aspx#when – mrtaikandi Dec 20 '14 at 09:58
  • He's doing just what your first code snippet shows. Exactly that. – usr Dec 20 '14 at 15:41