简体   繁体   English

在.NET中在后台取消SQL查询

[英]Cancelling SQL Query in background in .NET

I'm designing a small desktop app that fetches data from SQL server. 我正在设计一个小型桌面应用程序,该应用程序可以从SQL Server提取数据。 I used BackgroundWorker to make the query execute in background. 我使用BackgroundWorker使查询在后台执行。 The code that fetches data generally comes down to this: 提取数据的代码通常可以归结为:

public static DataTable GetData(string sqlQuery)
{
    DataTable t = new DataTable();

    using (SqlConnection c = new SqlConnection(GetConnectionString()))
    {
        c.Open();

        using (SqlCommand cmd = new SqlCommand(sqlQuery))
        {
            cmd.Connection = c;
            using (SqlDataReader r = cmd.ExecuteReader())
            {
                t.Load(r);
            }
        }
    }
    return t;
}

Since query can take up 10-15 minutes I want to implement cancellation request and pass it from GUI layer to DAL. 由于查询可能需要10到15分钟,因此我想实施取消请求并将其从GUI层传递到DAL。 Cancellation procedure of BackroundWorker won't let me cancel SqlCommand.ExecuteReader() beacuse it only stops when data is fetched from server or an exception is thrown by Data Provider. BackroundWorker取消过程不会让我取消SqlCommand.ExecuteReader()因为它仅在从服务器获取数据或数据提供者引发异常时才停止。 I tried to use Task and async/await with SqlCommand.ExecuteReaderAsync(CancellationToken) but I am confused where to use it in multi-layer app (GUI -> BLL -> DAL). 我试图使用TaskSqlCommand.ExecuteReaderAsync(CancellationToken) async/await ,但是我很困惑在多层应用程序(GUI-> BLL-> DAL)中使用它的位置。

Have you tried using the SqlCommand.Cancel() method ? 您是否尝试过使用SqlCommand.Cancel()方法?

Aproach: encapsulate that GetData method in a Thread/Worker and then when you cancel/stop that thread call the Cancel() method on the SqlCommand that is being executed. 方法:将GetData方法封装在线程/工作器中,然后在您取消/停止该线程时,对正在执行的SqlCommand调用Cancel()方法。

Here is an example on how to use it on a thread 这是有关如何在线程上使用它的示例

using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading;

class Program
{
    private static SqlCommand m_rCommand;

    public static SqlCommand Command
    {
        get { return m_rCommand; }
        set { m_rCommand = value; }
    }

    public static void Thread_Cancel()
    {
        Command.Cancel();
    }

    static void Main()
    {
        string connectionString = GetConnectionString();
        try
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();

                Command = connection.CreateCommand();
                Command.CommandText = "DROP TABLE TestCancel";
                try
                {
                    Command.ExecuteNonQuery();
                }
                catch { }

                Command.CommandText = "CREATE TABLE TestCancel(co1 int, co2 char(10))";
                Command.ExecuteNonQuery();
                Command.CommandText = "INSERT INTO TestCancel VALUES (1, '1')";
                Command.ExecuteNonQuery();

                Command.CommandText = "SELECT * FROM TestCancel";
                SqlDataReader reader = Command.ExecuteReader();

                Thread rThread2 = new Thread(new ThreadStart(Thread_Cancel));
                rThread2.Start();
                rThread2.Join();

                reader.Read();
                System.Console.WriteLine(reader.FieldCount);
                reader.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    static private string GetConnectionString()
    {
        // To avoid storing the connection string in your code, 
        // you can retrieve it from a configuration file.
        return "Data Source=(local);Initial Catalog=AdventureWorks;"
            + "Integrated Security=SSPI";
    }
}

You can only do Cancelation checking and Progress Reporting between Distinct lines of code. 您只能在不同的代码行之间进行取消检查和进度报告。 Usually both require that you disect the code down to the lowest loop level, so you can do both these things between/in the loop itterations. 通常,两者都要求您将代码分解到最低的循环级别,因此您可以在循环触发之间/之中进行这两项操作。 When I wrote my first step into BGW, I had the advantage that I needed to do the loop anyway so it was no extra work. 当我写到BGW的第一步时,我的优势是无论如何我都需要执行循环,所以这不需要额外的工作。 You have one of the worse cases - pre-existing code that you can only replicate or use as is. 您遇到的最坏情况之一是-只能复制或按原样使用的预先存在的代码。

Ideal case: 理想情况:

This operation should not take nearly as long is it does. 此操作应该花的时间不多。 5-10 minutes indicates that there is something rather wrong with your design. 5至10分钟表示您的设计存在某些问题。

If the bulk of the time is transmission of data, then you are propably retreiving way to much data. 如果大部分时间是在传输数据,那么您很可能会撤回大量数据的方法。 Retrieving everything to do filtering in the GUI is a very common mistake. 检索所有内容以在GUI中进行过滤是一个非常常见的错误。 Do as much filtering in the query as possible. 尽可能在查询中进行过滤。 Usign a Distributed Database might also help with transmission performance. Usign分布式数据库也可能有助于提高传输性能。

If the bulk of the time is processing as part of the query operation (complex Conditions), something in your general approach might have to change. 如果大部分时间都作为查询操作的一部分(复杂条件)进行处理,那么您一般方法中的某些内容可能必须更改。 There are various ways to trade off complex calculation with a bit of memory on the DBMS side. 在DBMS端有一些内存可以通过多种方式来权衡复杂的计算。 Views afaik can cache the results of operations, while still maintaining transactional consistency. 视图afaik可以缓存操作结果,同时仍保持事务一致性。

But it really depends what your backend DB/DBMS and use case are. 但这实际上取决于您的后端DB / DBMS和用例。 A lot of the use SQL as Query Language. 很多使用SQL作为查询语言。 So it does not allow us to predict wich options you have. 因此,它不允许我们预测您拥有的选择。

Second best case: 第二好的情况:

The second best thing if you can not cut it down, would be if you had the actually DB access code down to the lowest loop and would do progress reporting/cancelation checking on it. 如果您不能削减它的第二件事,那就是您是否将实际的数据库访问代码缩减到最低的循环,并对其进行进度报告/取消检查。 That way you could actually use the existing Cancelation Token System inherent in BGW. 这样,您实际上可以使用BGW中固有的现有取消令牌系统。

Everything else 其他一切

Using any other approach to Cancelation is really a fallback. 使用任何其他取消方法实际上是一个后备。 I wrote a lot on why it is bad, but felt that this might work better if I focus on the core issue - likely something wrong in design of he DB and/or Query. 我写了很多关于它为什么不好的文章,但是我觉得如果专注于核心问题,这可能会更好-在DB和/或Query的设计中可能出了点问题。 Because those might well eliminate the issue altogether. 因为这些完全可以消除问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM