EF6 ToListAsync()取消长查询不起作用 [英] EF6 ToListAsync() Cancel Long Query not working

查看:341
本文介绍了EF6 ToListAsync()取消长查询不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Entity Framework WinForms应用程序,并试图在与UI分开的线程上运行查询,并允许用户取消长查询。
我已经已经问过一个问题有关如何正确实施此问题,并且在那里可能仍然不正确,但是我当前的问题是如何允许用户取消长期运行的EF6查询?

I am working on an Entity Framework WinForms app and am trying to run queries on a thread separate from the UI and allow the user to cancel long queries. I've already asked one question about how to implement this correctly and might still be incorrect there too, but my current question is how do I allow a user to cancel a long-running EF6 query?

我发现了,但似乎仍无法正常工作...同样,可能是我将原始零件编程错误(从第一个问题开始),但是我的问题是我该如何允许用户单击取消按钮将停止对数据库的长时间查询?

I found links along the lines of this, but still can't seem to get this working well... Again, it may be that I programmed the original part wrong (as from my first question), but my question is how do I allow a user to click a cancel button that would stop a long query on the DB?

我当前的(相关)代码如下...

My (relevant) current code is as follows...

Private cts As New CancellationTokenSource

Private Sub Cancel_Click(sender As Object, e As EventArgs) Handles Cancel.Click
    cts.Cancel()
End Sub

尝试1(在Task中添加取消令牌) :

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim y As New List(Of DBData)

    Try
        Var1 = "Var1"

        Dim qry As Task(Of List(Of DBData)) = Task.Run(Function() GetDBData(Var1), cts.Token))

        Try
            y = Await qry
            cts.Token.ThrowIfCancellationRequested()
        Catch ex As OperationCanceledException
            ' ** ONLY REACH HERE AFTER QUERY PROCESSES!! **
        End Try
End Sub






尝试2(在EF ToListAsync()方法中添加取消令牌)


Attempt 2 (add cancellation token in EF ToListAsync() method):

Private Async Function GetDBData(Var1 As String, ct As CancellationToken) As Task(Of List(Of DBData))

    Dim retval As List(Of DBData)

    Using x As New DBContext
        Try
            retval = Await (From rw In x.DBData
                             Where rw.Val1= Val1
                             Select rw).ToListAsync(ct)
            ct.ThrowIfCancellationRequested()

            Return retval

        Catch ex As Exception
            ' ** ONLY REACH HERE AFTER QUERY PROCESSES!! **
            MsgBox(ex.Message)
            Return Nothing
        End Try
    End Using

End Function






我希望我的解释/代码对2之间的区别有意义...任何帮助将不胜感激! -即使在VB中,我对VB / C#解决方案也同样满意。


I hope my explanation / code posted makes sense in the differences between the 2... Any help would be greatly appreciated! - And even though this is in VB, I'm equally comfortable with VB / C# solutions.

谢谢!

推荐答案

这是EF团队从未修复过的错误。

It is a bug that was never fixed by the EF team sadly.

实际上,这是整个EF 6异步实现的整体弱点。

In fact it is an overall weakness in the whole EF 6 Async implementation.

开发团队已竭尽所能,通过使用内部类InternalContext等抽象化了对底层SqlCommand的所有访问。

The dev team have done their very best to abstract away any access to the underlying SqlCommand by using internal class InternalContext etc.

他们没有做的是将CancellationToken的Cancel()操作连接到SqlCommand.Cancel。这是一个明显的错误,非常令人沮丧。

What they have failed to do is to connect the Cancel() action of your CancellationToken to the SqlCommand.Cancel. This is a clear bug and very frustrating.

如果您的查询返回行,则它们的代码有效,因为任务将在迭代后返回,但这是一项非常可怜的工作。

If your query returns rows then their code works because the task will return after an iteration, but this a very poor effort.

internal static Task<List<T>> ToListAsync<T>(this IDbAsyncEnumerable<T> source, CancellationToken cancellationToken)
    {
      TaskCompletionSource<List<T>> tcs = new TaskCompletionSource<List<T>>();
      List<T> list = new List<T>();
      IDbAsyncEnumerableExtensions.ForEachAsync<T>(source, new Action<T>(list.Add), cancellationToken).ContinueWith((Action<Task>) (t =>
      {
        if (t.IsFaulted)
          tcs.TrySetException((IEnumerable<Exception>) t.Exception.InnerExceptions);
        else if (t.IsCanceled)
          tcs.TrySetCanceled();
        else
          tcs.TrySetResult(list);
      }), TaskContinuationOptions.ExecuteSynchronously);
      return tcs.Task;
    }

可以通过向IDbAsyncEnumerable添加逻辑来处理取消或创建逻辑来解决此问题。新接口IDbCancellableAsyncEnumerable,即,一个提供了Cancel方法的类,该方法在内部访问正在执行的SqlCommand并调用Cancel。

This could be fixed by adding logic to the IDbAsyncEnumerable to handle cancellation or creating a new interface IDbCancellableAsyncEnumerable, that is, a class that exposes a Cancel method that internally accesses the executing SqlCommand and calls Cancel.

由于他们没有对此感到烦恼,我怀疑这不会很快发生,因为这可能需要大量工作。

As they haven't bothered with this I suspect this will not be happening any time soon as it would be probably quite a lot of work.

注意:即使您尝试使用ExecuteSqlCommandAsync(),也仍然无法终止SqlCommand。

Note: Even if you try to use ExecuteSqlCommandAsync() you still have no way to kill the SqlCommand.

这篇关于EF6 ToListAsync()取消长查询不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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