Entity Framework with Asynchronous Controllers in Web api / MVC

I have a small code example:

public class ValueController : ApiController { private EstateContext _db; public ValueController() { _db = new EstateContext(); } [HttpPost] public async void DoStuff(string id) { var entity = await _db.Estates.FindAsync(id); //now our method goes out and Dispose method is calling //returns here after disposing _db.SaveChanges(); // _db is disposed } protected override void Dispose(bool disposing) { base.Dispose(disposing); _db.Dispose(); } } 

Each ApiController / Controller implements an IDisposable interface. Therefore, in the Dispose method, I want to free up any resources such as DbContext. But if async is used, this Dispose method is called when the wait first appears. Therefore, after waiting, I already have DbContext installed. So what is the best way to recycle EF contexts when using async? Turns out you can't rely on the Dispose method in the controller?

+7
c # asp.net-mvc asp.net-web-api entity-framework
source share
2 answers

But if async is used, this Dispose method is called upon the first appearance.

@ The answer to Konstantins is correct, but let me elaborate on why this is happening. When you use the async void method, you basically create the fire and forget semantics to call the method, because any caller of this method cannot asynchronously wait on it with await , since it returns void and not the expected form (for example, Task ).

Thus, although the WebAPI supports asynchronous methods, when you call your action, it seems like it was a synchronous void return method, and then the ASP.NET runtime continues to control your controller, as it assumes, done with the action.

When exposing a Task or Task<T> you explicitly tell the caller, "Listen, this method is asynchronous and will eventually return a value in the future." ASP.NET runtime knows that your controller has not yet completed a call to its action and expects the action to actually complete.

This is why the challenge is this:

 [HttpPost] public async Task DoStuffAsync(string id) { var entity = await _db.Estates.FindAsync(id); _db.SaveChanges(); } 

Works.

As a side note - EF DbContext should be used and removed as soon as possible. Using them as a global variable for several actions is a bad idea, since they are not thread safe . I would suggest another template in which each action is initialized and places a DbContext :

 [HttpPost] public async Task DoStuffAsync(string id) { using (var db = new EstateContext()) { var entity = await db.Estates.FindAsync(id); db.SaveChanges(); } } 

As @Wachburn noted in the comments, this approach is really less tested. If you guarantee that your controller and action will be deleted after completing each action and reusing the context, you can enter DbContext through the DI container.

+10
source share

You need to create a new instance of your EstateContext inside the async method.

 [HttpPost] public async void DoStuff(string id) { EstateContext db = new EstateContext(); var entity = await db.Estates.FindAsync(id); db.SaveChanges(); } 

However, I believe that if you change the type of the return action of your controller to Task<ActionResult> , then you can reuse the context that is a member of the controller.

 [HttpPost] public async Task<ActionResult> DoStuff(string id) { var entity = await _db.Estates.FindAsync(id); _db.SaveChanges(); } 
+4
source share

All Articles