entity framework在Web api / MVC中使用异步控制器进行处理

我有这个小代码示例:

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(); } } 

每个ApiController / Controller都实现了IDisposable接口。 所以在Dispose方法中我想释放任何资源,比如DbContext。 但是如果使用async,则此Dispose方法在第一次出现await时调用。 所以在等待之后我已经处理了DbContext。 那么在使用异步时处理EF上下文的最佳方法是什么? 事实certificate,在控制器中不可能依赖Dispose方法?

但是如果使用async,则此Dispose方法在第一次出现await时调用。

@Konstantins的回答是正确的,但请允许我详细说明为什么会发生这种情况。 当你使用async void方法时,你基本上是在为方法调用创建一个“fire and forget”语义,因为这个方法的任何调用者都不能通过await异步等待它,因为它返回void而不是表单一个等待的(如Task )。

因此,尽管WebAPI确实支持异步方法,但在调用您的操作时,它似乎是一个同步的void返回方法,然后ASP.NET运行时继续处理您的控制器,因为它假定您已完成操作。

当暴露TaskTask ,您明确告诉调用者“监听,此方法是异步的,最终将返回一个值” 。 ASP.NET运行时知道您的控制器尚未完成调用其操作,并等待实际完成操作。

这就是为什么像这样的电话:

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

作品。

作为旁注 – EF DbContext意味着尽快使用和处理。 将它们用作多个动作的全局变量是一个坏主意,因为它们也不是线程安全的 。 我会建议一个不同的模式,每个动作初始化并处理DbContext

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

正如@Wachburn在评论中指出的那样,这种方法确实不太可测试。 如果确保在每个操作完成后处理控制器和操作并且不再重复使用上下文,则可以通过DI容器注入DbContext

您需要在async方法中创建EstateContext的新实例。

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

但是,我相信如果您将控制器操作的返回类型更改为Task那么您应该能够重用作为控制器成员的上下文。

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