在Web api/MVC中使用异步控制器处理的实体框架 [英] Entity Framework disposing with async controllers in Web api/MVC

查看:98
本文介绍了在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.但是,如果使用了异步,则此Dispose方法在第一次出现await时调用.因此,在等待之后,我已经处置了DbContext. 那么,使用异步时处置EF上下文的最佳方法是什么?事实证明,不可能依靠控制器中的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?

推荐答案

但是,如果使用异步,则此Dispose方法在第一次出现时调用 等待.

But if async is used, this Dispose method calls at first occurrence of await.

@Konstantins答案是正确的,但请允许我详细说明为什么会这样.当使用async void方法时,基本上是在方法调用中创建即发即弃"的语义,因为此方法的任何调用者本身都无法通过await异步地等待它,因为它返回,而不是一种等待形式(例如Task).

@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).

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

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.

公开TaskTask<T>时,您要明确告诉调用方侦听,此方法是异步的,将来会最终返回值".. ASP.NET运行时知道您的控制器尚未完成调用其动作,并等待该动作的实际完成.

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

工作.

作为旁注-EF DbContext旨在尽快使用和处置.将它们用作多个操作的全局变量不是一个好主意,因为它们也不是线程安全的.我会建议一个不同的模式,其中每个动作都会初始化并处理DbContext:

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

正如@Wachburn在评论中指出的那样,这种方法的确难以测试.如果您确保在每个动作完成后就处置了控制器和动作,并且没有重复使用上下文,那么可以安全地通过DI容器注入DbContext.

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.

这篇关于在Web api/MVC中使用异步控制器处理的实体框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆