【问题标题】:Queuing EF queries排队 EF 查询
【发布时间】:2016-06-17 20:19:06
【问题描述】:

在 Windows 桌面应用程序中,上下文与视图一样长,避免“在前一个异步操作完成之前在此上下文上启动第二个操作”错误的最佳方法是什么?

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

但是,想象一下我有这个:

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

private async void SomeMethodB()
{
    await Context.GetB();
}

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

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

【问题讨论】:

  • 存储库模式旨在将您的域模型与您的业务逻辑分开,这在此用例中对您没有帮助。在您的示例中,您有一个业务规则问题,您正在使用异步等待。例如,当您调用 SomeMethodA 时,方法 someMethodB() 的方法按钮将被禁用,直到方法 A 完成或等待 TaskA 通过使用帮助您继续的 TaskCompletionSource 完成。
  • @BassamAlugili SomeMethodB 可以通过代码 sn-p 调用,例如来自某个侦听器、计时器等的某些事件。所以,我不能依赖于禁用任何东西,它不必是用户交互。
  • @StephenCleary 已经给你答案了。他是 C# 中的多任务之王。

标签: c# entity-framework entity-framework-6 async-await


【解决方案1】:

避免“在前一个异步操作完成之前在此上下文上启动第二个操作”错误的最佳方法是什么?

始终await您的异步调用。

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

它们将异步执行,而不是同步执行。但是,它们将串行(一次一个)执行,我认为这就是您的意思。

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

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

代码执行返回 UI 线程

这里没有 UI 线程。执行只是返回给调用者。

【讨论】:

  • 关于没有事件处理程序,假设我有这种情况:我有一个侦听器,它每 x 秒检查一些信息。因此,每 x 秒就会引发一个事件。现在,用户启动一些触发 SomeMethodA 的操作,然后侦听器立即获取一些需要执行 SomeMethodB 的信息。显然这两个调用将重叠,这将引发异常。到目前为止,我在互联网上收集到的信息,似乎 AsyncLock 可能是这里的解决方案,我做一些测试后会看到。
  • @Goran:可以,可以使用AsyncLock来实现异步代码的互斥。
【解决方案2】:

https://msdn.microsoft.com/en-us/data/jj729737.aspx 和几个 SO 答案中所述,您最好将上下文设置为使用它的方法的本地化。我不太了解 await,但我的第一个猜测是:

private async void SomeMethodA()
{
    using (var myContext = new Context()) // or, possibly, new Entities()
    {
        await myContext.GetA();
    }
}

【讨论】:

  • 如果您阅读链接的同一篇文章,您会注意到他们建议在桌面应用程序(前 WPF)中为每个表单使用上下文实例 - 长期存在,而不是短期存在。
猜你喜欢
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
  • 2015-02-20
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2020-03-29
相关资源
最近更新 更多