繁体   English   中英

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

[英]Cancelling SQL Query in background in .NET

我正在设计一个小型桌面应用程序,该应用程序可以从SQL Server提取数据。 我使用BackgroundWorker使查询在后台执行。 提取数据的代码通常可以归结为:

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;
}

由于查询可能需要10到15分钟,因此我想实施取消请求并将其从GUI层传递到DAL。 BackroundWorker取消过程不会让我取消SqlCommand.ExecuteReader()因为它仅在从服务器获取数据或数据提供者引发异常时才停止。 我试图使用TaskSqlCommand.ExecuteReaderAsync(CancellationToken) async/await ,但是我很困惑在多层应用程序(GUI-> BLL-> DAL)中使用它的位置。

您是否尝试过使用SqlCommand.Cancel()方法?

方法:将GetData方法封装在线程/工作器中,然后在您取消/停止该线程时,对正在执行的SqlCommand调用Cancel()方法。

这是有关如何在线程上使用它的示例

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";
    }
}

您只能在不同的代码行之间进行取消检查和进度报告。 通常,两者都要求您将代码分解到最低的循环级别,因此您可以在循环触发之间/之中进行这两项操作。 当我写到BGW的第一步时,我的优势是无论如何我都需要执行循环,所以这不需要额外的工作。 您遇到的最坏情况之一是-只能复制或按原样使用的预先存在的代码。

理想情况:

此操作应该花的时间不多。 5至10分钟表示您的设计存在某些问题。

如果大部分时间是在传输数据,那么您很可能会撤回大量数据的方法。 检索所有内容以在GUI中进行过滤是一个非常常见的错误。 尽可能在查询中进行过滤。 Usign分布式数据库也可能有助于提高传输性能。

如果大部分时间都作为查询操作的一部分(复杂条件)进行处理,那么您一般方法中的某些内容可能必须更改。 在DBMS端有一些内存可以通过多种方式来权衡复杂的计算。 视图afaik可以缓存操作结果,同时仍保持事务一致性。

但这实际上取决于您的后端DB / DBMS和用例。 很多使用SQL作为查询语言。 因此,它不允许我们预测您拥有的选择。

第二好的情况:

如果您不能削减它的第二件事,那就是您是否将实际的数据库访问代码缩减到最低的循环,并对其进行进度报告/取消检查。 这样,您实际上可以使用BGW中固有的现有取消令牌系统。

其他一切

使用任何其他取消方法实际上是一个后备。 我写了很多关于它为什么不好的文章,但是我觉得如果专注于核心问题,这可能会更好-在DB和/或Query的设计中可能出了点问题。 因为这些完全可以消除问题。

暂无
暂无

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

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