I have this little sample of code:
public class ValueController : ApiController
{
private EstateContext _db;
public ValueController()
{
But if async is used, this Dispose method calls at first occurrence of await.
@Konstantins answer is correct, but allow me to elaborate a bit on why that happens. When you use an async void
method, you're basically creating a "fire and forget" semantics to your method call, because any caller of this method can't itself asynchronously wait on it with await
, as it returns void
and not a form of an awaitable (such as a Task
).
Thus, although WebAPI does support asynchronous methods, when invoking your action it seems as if it was a synchronous void
returning method, and then the ASP.NET runtime goes on to dispose your controller, because it assumes that you're done with the action.
When exposing a Task
or Task<T>
, you're explicitly telling the caller "Listen, this method is asynchronous an will eventually return a value in the future". The ASP.NET runtime knows your controller hasn't finished invoking his action, and awaits upon the actual completion of the action.
This is why a call like this:
[HttpPost]
public async Task DoStuffAsync(string id)
{
var entity = await _db.Estates.FindAsync(id);
_db.SaveChanges();
}
Works.
As a side note - EF DbContext
are meant to be used and disposed as soon as possible. Using them as a global variable for multiple actions is a bad idea, as they are not thread-safe either. I would suggest a different pattern where each action initializes and disposes the DbContext
:
[HttpPost]
public async Task DoStuffAsync(string id)
{
using (var db = new EstateContext())
{
var entity = await db.Estates.FindAsync(id);
db.SaveChanges();
}
}
As pointed out by @Wachburn in the comments, this approach is indeed less testable. If you ensure that your controller and action are disposed after each action is complete and there's no re-use of the context, you're safe to inject the DbContext
via a DI container.
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 return type of your controller action to Task<ActionResult>
then you should be able to 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();
}