【问题标题】:Cancel Long Running Entity Framework 6 async requests取消长时间运行的实体框架 6 异步请求
【发布时间】:2015-11-08 02:09:18
【问题描述】:

我在 WPF 应用程序中使用实体框架 6 (DbContext),我想找到一种方法来正确取消 async 数据加载方法 (ToListAsync & FirstOrDefaultAsync) 所以我可以立即开始另一个请求。

我一直在尝试坚持每个表单(在我的例子中是选项卡)标准的单一上下文,到目前为止,我一直在通过确保 UI 被禁用来处理这些调用的非线程安全性质在请求期间,因此用户在进行中时无法启动任何新请求。但是,我现在遇到了一个用例,这是不可能的。我需要在一些长时间运行的请求期间保持 UI 响应,为此,我需要一种方法来取消当前请求并立即启动另一个请求。

我尝试利用添加到Async 方法的CancellationToken 支持,但我发现当我取消请求时,它实际上并没有取消任何内容。它会正确抛出OperationCanceledException,但请求仍在进行中,当我尝试在此之后发出另一个请求时,我仍然得到NotSupportedException (A second operation started on this context...)

我正在使用更改跟踪,因此更改应用以对每个请求使用新的上下文是不现实的。

此外,我已经通过处理当前上下文并在每次该特定视图模型在一个正在进行的情况下发出请求时创建一个新上下文来暂时解决此问题。这从技术上解决了我的问题,但我想知道是否有办法在保持相同上下文的同时做到这一点。

那么,有人有这方面的经验吗?我很难相信我是第一个遇到这个问题的人,但是我在这里找到的所有其他类似问题的答案要么建议我使用CancellationToken(它不能正常工作)要么是有点旧,不适用于Async 方法。

编辑 1:

由于还没有人回答这个问题,我真的开始想知道我的选择是什么。一点背景。我正在将 Silverlight 应用程序转换为 WPF。Silverlight 应用程序使用带有 EF 4.1 的 WCF RIA 服务,但是对于 WPF 应用程序,我们决定只使用 EF6.1。

使用 Silverlight 和 WCF,您一次可以进行的异步调用数量没有限制,而且我们实际上为整个应用程序提供了一个上下文(我知道这很糟糕,但很简单,而且我们从来没有遇到过任何问题) .我们只是直接绑定到实体,并使用更改跟踪来保存用户所做的更改。

在现实世界的应用程序中,使用 EF 6.1 和 Async 方法在 WPF 中是否没有办法做到这一点,有时,您只需要取消应用程序正在执行的操作并执行什么操作用户想要的,而不是崩溃和燃烧?

【问题讨论】:

  • 您能否分享一下您如何使用取消令牌(将其传递给查询以及如何触发它)的代码?
  • 此时我已将其删除,但我所拥有的是视图模型实例化了一个新的 CancellationTokenSource。然后它将 CancellationToken 传递给 ToListAsync、FirstOrDefaultAsync 和 SaveChangesAsync 方法。需要时,VM 会在 CancallationTokenSource 上调用 Cancel,VM 中发出请求的代码被包含在处理 OperationCanceledException 的 try catch 中。
  • 一旦除了 sql 连接或事务级别的超时之外,没有办法取消对数据库的实际查询。您还可以确保调用“ThrowIfCancellationRequested”以确保在 SaveChangesAsync 或 ToListAsync 之后引发 OperationCancelledException。我将“否”放在引号中,因为我想您可以在 db 级别杀死正在运行的进程的 spid,这将强制回滚事务,但它仍会运行直到回滚完成。
  • 异常肯定是由 EF 异步方法引发的。我输入了一些日志,我会得到以下内容: 17:50:57:028 [DEBUG] GetListAsync requested 17:50:58:032 [DEBUG] Cancel requested 17:50:58:160 [DEBUG] ] OpeationCanceledException 捕获 17:50:59:028 [DEBUG] GetListAsync 响应:..

标签: c# wpf entity-framework asynchronous


【解决方案1】:

现在发布我的解决方案。我不是一个狂热的粉丝,但我已经能够开始工作,不需要从头开始完全重写这个应用程序。

我现在在访问或保存跟踪实体的存储库方法中使用来自AsyncExAsyncLock 类。每个 DbContext 对象都使用自己的锁,因此我不会阻止来自其他上下文或未跟踪实体的其他调用。

例如,我的存储库中的 GetListAsync 方法:

public async virtual Task<IList<T>> GetListAsync(IDbContext context,
                                                 Expression<Func<T, bool>> where,
                                                 IOrderByClause<T>[] orderBy = null,
                                                 params Expression<Func<T, object>>[] navigationProperties)
{
    using (await context.ContextLock.LockAsync())
    {
        IList<T> list = await GetListQuery(context, where, orderBy, navigationProperties).ToListAsync();
        return list;
    }
}

GetListQuery 使用导航属性以及 where 和 order by 子句创建查询。

我可能还会使用 CancellationToken 添加超时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多