I have this little sample of code:
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();
}
}
Every ApiController/Controller implements IDisposable interface. So in the Dispose method I want to free up any resources such as DbContext. But if async is used, this Dispose method calls at first occurrence of await. So after await I have DbContext already disposed.
So what is the best way to dispose EF Contexts when async is used? It turns out that it is not possible to rely on Dispose method in controller?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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.
Method 2
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();
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0