首页 > 解决方案 > 使用 ProgressBar 监控 SQL 查询的执行进度,如果 sql 表的 field_id 增加 1 则更新进度条百分比

问题描述

我完全不知道如何实现这一点。在 StackOverflow 上搜索后,我做了一些努力,但这对我来说显然是前所未有的。

为了首先写这个问题,我假设不能根据 sql 查询的进度更新进度条,因为它不知道 sql 查询的进度如何。

首先,我将描述我的概念。我有一个 MainWindow 类,我的应用程序是在其上设计的。在这个 MainWindow 类上,我有一个名为“ Run ”的按钮。单击此按钮时,它会通过调用 7 个存储过程来运行非常耗时的 sql 查询。具体来说,查询大约需要 4 分钟才能执行。所以我最初的想法是创建一个进度条,向用户显示查询的执行进度,而不是冻结应用程序屏幕和 w8 4 分钟。

主窗口类

namespace TestEnvironment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public static class StringExtensions
    {
        public static string Args(this string str, params object[] args)
        {
            return String.Format(str, args);
        }
    }

    public partial class MainWindow : Window
    {   
        private LoginScreen _LoginScreen;
        private CreateProjectScreen _CreateProjectScreen;

        public MainWindow(LoginScreen loginscreen, CreateProjectScreen createprojectscreen)
        {
            InitializeComponent();
            _LoginScreen = loginscreen; //2nd window
            _CreateProjectScreen = createprojectscreen; //3rd window
        }

    private void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Application.Current.Dispatcher.Invoke((Action)delegate {
                    ProgressBarTemplate win_progressbar = new ProgressBarTemplate();
                    win_progressbar.Show();
                }); //Added the Application.Current Dispatcher because of the STA thread error

                //Some extra code after the progress-bar is completed.
                //...//

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
        }
}

ProgressBarTemplate 窗口类

using System.Windows;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;
using System.Data.SqlClient;

namespace TestEnvironment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class ProgressBarTemplate : Window
    {
        private CreateProjectScreen _CreateProjectScreen;
        private LoginScreen _LoginScreen;

        public ProgressBarTemplate()
        {
            InitializeComponent();
        }

        public static int RunCalculationsMethod(string connectionstring, string foldername)
        {
            bool exists = Directory.Exists(foldername);

            if (!exists)
            {
                Directory.CreateDirectory(foldername);
            }

            try
            {
                using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
                {
                    var calculations_query = String.Format(
                    "EXEC [dbo].[procedure_1]; \n" +
                    "EXEC [dbo].[procedure_2]; \n" +
                    "EXEC [dbo].[procedure_3] " + "'{0}', " + "';' " + ";\n" +
                    "EXEC [dbo].[procedure_4] " + "'{0}', " + "';' " + ";\n" +
                    "EXEC [dbo].[procedure_5]; \n" +
                    "EXEC [dbo].[procedure_6] " + "'{0}', " + "';' " + ";\n" +
                    "--EXEC [dbo].[procedure_7]; \n" +
                    "--EXEC [dbo].[procedure_8] " + "'{0}', " + "';' " + ";\n" +
                    "EXEC [dbo].[procedure_9]"
                    , foldername);

                    using SqlCommand sqlCommand = new SqlCommand(calculations_query, sqlConnection);

                    sqlConnection.Open();

                    sqlCommand.CommandTimeout = 60 * 10;

                    int NumbderOfRecords = sqlCommand.ExecuteNonQuery();

                    return NumbderOfRecords;
                }
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return -100;
            }
        }

        private void Window_ContentRendered(object sender, EventArgs e)
        {
            try
            {
                BackgroundWorker worker = new BackgroundWorker();
                worker.WorkerReportsProgress = true;
                worker.DoWork += worker_DoWork;
                worker.ProgressChanged += worker_ProgressChanged;
                worker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;

                worker.RunWorkerAsync();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
           
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {            
            int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=TestProgressBarDB;Integrated Security=SSPI", String.Format("C:\\folder_test\\"));
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pbStatus.Value = e.ProgressPercentage;
        }

        void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // This is called on the UI thread when the DoWork method completes
            // so it's a good place to hide busy indicators, or put clean up code

            try
            {
                this.Close();
                MessageBox.Show("DQ Calculations completed successfully", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OKCancel, MessageBoxImage.Error);
            }
        }
    }
}

进度条 xaml

<Window x:Class="TestEnvironment.ProgressBarTemplate"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        Title="ProgressBarTemplate"
        Height="100"
        Width="300"
        ContentRendered="Window_ContentRendered"
        ResizeMode="NoResize" 
        WindowStartupLocation="CenterScreen" 
        WindowStyle="None"
        BorderBrush="Black"
        BorderThickness="1">
    <Border Margin="1,1,1,1" BorderBrush="Black" BorderThickness="0.5">
        <Grid Margin="20,10,10,10" Focusable="False">
            <ProgressBar
                Style="{StaticResource MaterialDesignLinearProgressBar }"
                Minimum="0"
                Maximum="100"
                Name="pbStatus"
                Value="75"
                Height="15"
                Margin="0,10,0,0"
                Foreground="Green"
                Background="LightGray"
                BorderBrush="LightGray"
                Focusable="False"
                Padding="2"/>
            <TextBlock Text="{Binding ElementName=pbStatus, Path=Value, StringFormat={}{0:0}%}" 
                       Style="{StaticResource MaterialDesignButtonTextBlock}"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       FontSize="13"
                       FontWeight="Medium"
                       Foreground="Black"
                       Margin="0,7,0,0"/>
        </Grid>
    </Border>
</Window>

这是我目前正在运行的代码。但是,即使我的查询开始正确执行并显示进度条,进度百分比也不会改变。它停留在 75%(默认 xaml 文本添加到我的进度条)4 分钟,直到完成。

在此处输入图像描述

我的目标是首先使进度条工作(意思是改变百分比值)。第二个目标是根据问题标题中所述的自定义语句更改栏的进度。

每 5 秒检查一次 SQL 日志表,只有当 step 的 field_id 增加 1 时,才会更新进度条 0.88%。0.88% 是步数/100% = 88 步/100。88 步是必要的完成整个查询才能成功。所以查询从 id 1 的第 1 步开始,5 秒后我想检查表,如果 id 为 2 的第 2 步完成,则将进度条增加 0.88%。然后在 5 秒后重新检查 sql 表,如果写入 id 为 3 的步骤 3,则将进度条再更改 0.88%,现在总共 1.76%。这样做直到 100%。

我查询 field_id 所在的 SQL 表

public List<int> GetFieldId(string connectionstring)
{
    List<int> fieldid= new List<int>();

    using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
    {
        var query = "SELECT TOP(1) l.LOG_ID FROM LOG_DETAILS l ORDER BY 1 DESC"

        sqlConnection.Open();

        using (SqlCommand sqlCommand = new SqlCommand(query , sqlConnection))
        {
            using (SqlDataReader reader = sqlCommand.ExecuteReader())
            {
                while (reader.Read())
                {
                    fieldid.Add(reader.GetInt32(0));
                }
                reader.Close();
            }
            sqlCommand.Cancel();
        }
    }
    return fieldid;
}

像这样调用方法

int number_field_id = GetFieldId("Server=localhost;Database=TestProgressBarDB;Integrated Security=SSPI");

因此,每 5 秒执行一次 GetFieldId() 方法,获取顶部的 field_id,如果它从上一次检查(过去 5 秒)增加 1,则进度条将增加 0.88%。

请考虑我写这个问题的努力,因为我试图尽可能清楚地说明问题。

[1 可能的解决方案 - 但不是首选]

因为我知道在查询执行之前我会 w8 4 分钟,所以我可以做类似的事情

void worker_DoWork(object sender, DoWorkEventArgs e)
{ 
for (int i = 0; i < 100; i++)
    {
        (sender as BackgroundWorker).ReportProgress(i);
        Thread.Sleep(60*4 + 10); //+10 secs
    }
}

但这不是动态的,因为我不知道每次都是 4 分钟。它在我的本地机器上运行 4 分钟。

搜索的相关 SO 问题:

标签: c#sql-serverwpf

解决方案


推荐阅读