EntityFramework (6) and async ( waitingForActivati

2019-08-04 13:26发布

问题:

I've downloaded EF6 ( in order to use async)

So I wrote this simple method :

  public async Task<List<int>> MyasyncMethod()
      {
          var locations = await MyDumpEntities.AgeGroups.Select(f=>f.endYear).ToListAsync();
          return locations;
       }

   ...Later...


  DumpEntities1 MyDumpEntities = new DumpEntities1();
  var data = MyDumpEntities.AgeGroups.ToListAsync();
  MyasyncMethod().ContinueWith(s => { Response.Write("f"); });
  MyDumpEntities.Dispose();

But I don't see anything on the screen and when I inspect data I see this :

p.s. this is the ToListAsync signature

What am I missing ?

回答1:

Basing it of off the comments and the line that you have problem with:

var data = MyDumpEntities.AgeGroups.ToListAsync();

What will data type be? Task<List<AgeGroup>>. That's right, not List<AgeGroup>. So, you either have to mark the Page_Load as async (if possible):

public async void Page_Load(object sender, EventArgs e)
{
    using(var MyDumpEntities = new DumpEntities1())
    {
       var data = await MyDumpEntities.AgeGroups.ToListAsync();
    }     
}

Or wait for the execution somehow (continuation, blocking wait).

Another thing (someone else might want to correct that if I'm wrong), but since you're using continuation for the second call, I would be very careful of disposing the context outside of continuation. It might turn out that you dispose the context preemptively. In this particular case, you're not using the context in the continuation, but it looks suspicious...

So I'd either

MyasyncMethod().ContinueWith(s => { Response.Write("f"); MyDumpEntities.Dispose();});

Or just use async there too

var result = await MyasyncMethod();
Response.Write("f");
MyDumpEntities.Dispose();

and add Async="True" to the page directive



回答2:

Others have pointed out that the correct solution is to use await, but I don't see a good explanation of why.

There are two reasons why the original code is wrong. First, you're using ContinueWith without capturing the context in an ASP.NET application, so the continuation (the Response.Write call) does not have a request context and therefore has no response to write to.

await takes care of this for you by capturing the context before the await and using that to resume the rest of the method; in this case, it will capture an AspNetSynchronizationContext representing the current request/response.

The other reason is that asynchronous code will run concurrently. So, MyasyncMethod will begin executing, reach its await, and return an uncompleted task to Page_Load. Page_Load then attaches a continuation to that task and continues executing, disposing the context. So the context may be disposed while the ToListAsync request is still running.

await also fixes this for you because it will "pause" the Page_Load method until MyasyncMethod is complete.

As a final note, you should also consider these points when using async in ASP.NET:

  1. You must target .NET 4.5. Do not use Microsoft.Bcl.Async.
  2. You must set targetFramework to 4.5, or set UseTaskFriendlySynchronizationContext to true.
  3. (WebForms only) Set Page.Async to true.
  4. Consider using RegisterAsyncTask instead of await. I usually prefer await because different methods have more separation of concerns, but the ASP.NET team prefers RegisterAsyncTask because there is a single "synchronize" point after PreRender where the runtime waits for all the operations to complete. See this article for how to use RegisterAsyncTask.
  5. Build in your own request timeouts. Asynchronous ASP.NET requests do not automatically use the normal timeouts that are built-in to synchronous ASP.NET requests. There are two options:
    • Use the HttpRequest.TimedOut cancellation token.
    • (WebForms/RegisterAsyncTask only) You can add an asynchronous timeout by setting Page.AsyncTimeout and having your async method take a CancellationToken.


回答3:

ToListAsync returns a Task. Mark Page_Load as async, then you can use await in it.

Rough rule: If something returns a Task (contains "Async" in method name), you have to await it.

Also, when using async/await you don't have to use ContinueWith. Just await your own method and place the Write call in the next line.

DumpEntities1 MyDumpEntities = new DumpEntities1();
var data = await MyDumpEntities.AgeGroups.ToListAsync();
var dataFromMyMethod = await MyasyncMethod()
Response.Write("f");
MyDumpEntities.Dispose();

and add Async="True" to the page directive