C# 排队EF查询

C# 排队EF查询,c#,entity-framework,entity-framework-6,async-await,C#,Entity Framework,Entity Framework 6,Async Await,在windows桌面应用程序中,上下文与视图的寿命一样长,如何避免“在前一个异步操作完成之前在此上下文上启动第二个操作”错误 在我的视图模型中,我接收到一个repository实例(通过构造函数),它封装了一个DbContext的长期实例,当我需要加载一些数据时,例如,如果我从同一个方法加载数据,那么我可以在async方法中等待它们中的每一个,从而确保它们是同步执行的 然而,想象一下我有这样一个: private async void SomeMethodA() { await Cont

在windows桌面应用程序中,上下文与视图的寿命一样长,如何避免“在前一个异步操作完成之前在此上下文上启动第二个操作”错误

在我的视图模型中,我接收到一个repository实例(通过构造函数),它封装了一个DbContext的长期实例,当我需要加载一些数据时,例如,如果我从同一个方法加载数据,那么我可以在async方法中等待它们中的每一个,从而确保它们是同步执行的

然而,想象一下我有这样一个:

private async void SomeMethodA()
{
    await Context.GetA();
}

private async void SomeMethodB()
{
    await Context.GetB();
}
它们被声明为void,因为它们实际上是一些事件处理程序。因此,我的代码执行SomeMethodA(),这个数据库调用可能需要2秒钟。所以,代码执行返回到UI线程,然后某个东西(用户或代码)触发SomeMethodB(),这将引发上述异常


确保异步数据库调用排队的最佳方法是什么?存储库是处理此问题的最佳场所吗?

您最好将您的上下文设置为使用它的方法的本地上下文,如中所述和几个SO答案。我对Wait不太了解,但我的第一个猜测是:

private async void SomeMethodA()
{
    using (var myContext = new Context()) // or, possibly, new Entities()
    {
        await myContext.GetA();
    }
}
避免“在前一个异步操作完成之前在此上下文上启动第二个操作”错误的最佳方法是什么

始终
等待
您的异步调用

我可以在async方法中等待它们中的每一个,这确保了它们是同步执行的

它们将异步执行,而不是同步执行。然而,它们将被连续执行(一次一个),我想这就是你的意思

它们被声明为void,因为它们实际上是一些事件处理程序

这就是你的问题。他们必须等待。抱歉,它们不能是事件处理程序。您必须修改应用程序设计的这一部分。即使您解决了这个问题(例如,通过每个请求打开多个db连接),您仍然会遇到问题,原因是“异步操作仍挂起时异步模块或处理程序已完成”异常

代码执行返回到UI线程


这里没有UI线程。执行只会返回到调用方。

如果您阅读了链接的同一篇文章,您会注意到他们建议在桌面应用程序(ex WPF)中为每个表单使用上下文实例—长期有效,不是短期的。存储库模式被设计成将您的域模型与业务逻辑分开,这在这个用例中对您没有帮助。在您的示例中,您遇到了业务规则问题,您使用的是异步等待。例如,当您调用SomeMethodA时,someMethodB()方法的“方法”按钮将被禁用,直到方法A完成或使用TaskCompletionSource等待TaskA完成,这有助于您继续。@BassamAlugili someMethodB可以通过代码段调用,如来自某个侦听器、计时器等的某个事件。因此,我不能依赖于禁用任何东西,它不一定是用户交互。@StephenCleary已经给了你一个答案。他是C#中的多任务之王。关于无事件处理程序,假设我有这样一个场景:我有一个侦听器,它每x秒检查一次某些信息。因此,每x秒就会引发一个事件。现在,用户启动一些触发SomeMethodA的操作,然后侦听器立即获取一些需要执行SomeMethodB的信息。显然,这两个调用将重叠,这将引发一个异常。到目前为止,我在互联网上收集到的信息,似乎AsyncLock可能是这里的解决方案,在我做了一些测试之后就会看到。@Goran:是的,你可以使用
AsyncLock
来互斥异步代码。