C# 在winforms中运行长时间运行的Sql脚本并保持UI响应的最佳方法
我一直在为我的应用程序编写一个SQL安装程序,但没有给我太多的时间来处理它,因此它有点脏。我的UI线程从表单中收集值,将它们插入一系列SQL Scriptp,然后使用SMO逐个“异步”地将它们踢出。以下是UI线程上调用方法的示例:C# 在winforms中运行长时间运行的Sql脚本并保持UI响应的最佳方法,c#,sql-server,winforms,async-await,smo,C#,Sql Server,Winforms,Async Await,Smo,我一直在为我的应用程序编写一个SQL安装程序,但没有给我太多的时间来处理它,因此它有点脏。我的UI线程从表单中收集值,将它们插入一系列SQL Scriptp,然后使用SMO逐个“异步”地将它们踢出。以下是UI线程上调用方法的示例: private static bool BeginScriptExecution(Dictionary<string, string[]> scripts) { try {
private static bool BeginScriptExecution(Dictionary<string, string[]> scripts)
{
try
{
foreach (var script in scripts)
{
if (script.Key.Length > 0)
{
SqlConnection conn = new SqlConnection();
conn = ReplaceDatabaseName(GetConnectionString(), script.Value[0]);
if (TestSqlConnection())
{
if (ConfigurationManager.AppSettings["useSMO"] == "1")
{
if (!RunDatabaseCommandsSmo(conn, script.Key, script.Value[1]).Result)
{
throw new Exception("Script failed to run. Error in SMO functions.");
}
}
else
{
if (!RunDatabaseCommandsNonSmo(conn, script.Key, script.Value[1]))
{
throw new Exception("Script failed to run. Non-SMO related failure.");
}
}
}
else
{
throw new Exception("Connection not available. Script failed to run");
}
}
}
return true;
}
catch (Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
在我开始转向async之前,我在UI线程上运行所有程序,在脚本返回完成之前,表单会给我“没有响应”。一旦我使用了这种异步方式,应用程序的响应性就会提高一点,我也不会再没有响应了,但我不确定这种方法以及它是否正确使用了异步。有人能告诉我如何修改代码以使其正确工作吗?有4个脚本运行并且必须按特定顺序运行,这意味着脚本2在脚本1返回之前无法运行。使用异步任务的一个好方法是使用后台工作程序 请看下面的代码示例:
如果B.G.是你的选择,下面是一个例子:
使用系统;
使用系统集合;
使用系统组件模型;
使用系统图;
使用系统线程;
使用System.Windows.Forms;
命名空间BackgroundWorkerExample
{
公共类FibonacciForm:System.Windows.Forms.Form
{
私有int numberToCompute=0;
private int highestPercentageReached=0;
private System.Windows.Forms.NumericUpDown NumericUpDown 1;
private System.Windows.Forms.Button startAsyncButton;
private System.Windows.Forms.Button取消异步按钮;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label结果标签;
private System.ComponentModel.BackgroundWorker backgroundWorker1;
公共FibonacciForm()
{
初始化组件();
初始化BackgroundWorker();
}
//通过设置BackgroundWorker对象
//附加事件处理程序。
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork+=
新道工Venthandler(后台道工1_);
backgroundWorker1.RunWorkerCompleted+=
新RunWorkerCompletedEventHandler(
后台工作1_运行工作完成);
backgroundWorker1.ProgressChanged+=
新的ProgressChangedEventHandler(
背景工作人员1(变更);
}
private void startAsyncButton_单击(System.Object sender,
System.EventArgs(e)
{
//重置结果标签中的文本。
resultLabel.Text=String.Empty;
//禁用向上向下控制,直到
//异步操作完成。
this.numericUpDown1.Enabled=false;
//禁用“开始”按钮,直到
//异步操作完成。
this.startAsyncButton.Enabled=false;
//启用“取消”按钮,同时
//异步操作运行。
this.cancelAsyncButton.Enabled=true;
//从UpDown控件获取值。
numberToCompute=(int)numericUpDown1.Value;
//重置百分比跟踪的变量。
highestPercentageReached=0;
//启动异步操作。
backgroundWorker1.RunWorkerAsync(numberToCompute);
}
私有无效取消异步按钮\u单击(System.Object sender,
System.EventArgs(e)
{
//取消异步操作。
this.backgroundWorker1.CancelAsync();
//禁用“取消”按钮。
cancelAsyncButton.Enabled=false;
}
//此事件处理程序是实际,
//完成了可能耗时的工作。
私有void backgroundWorker1_DoWork(对象发送方,
道夫特(e)
{
//获取引发此事件的BackgroundWorker。
BackgroundWorker worker=发件人作为BackgroundWorker;
//分配计算结果
//到DoWorkEventArgs的结果属性
//对象。这将可用于
//RunWorkerCompleted eventhandler。
e、 结果=ComputeFibonacci((int)e.参数,worker,e);
}
//此事件处理程序处理
//后台操作。
私有void backgroundWorker1\u RunWorkerCompleted(
对象发送器,RunWorkerCompletedEventArgs(e)
{
//首先,处理引发异常的情况。
如果(例如错误!=null)
{
MessageBox.Show(例如Error.Message);
}
否则(如已取消)
{
//接下来,处理用户取消的情况
//手术。
//请注意,由于
//DoWork事件处理程序已取消
//可能尚未设置标志,即使
//已调用CancelAsync。
resultLabel.Text=“已取消”;
}
其他的
{
//最后,处理操作失败的情况
//成功了。
resultLabel.Text=e.Result.ToString();
}
//启用向上向下控制。
this.numericUpDown1.Enabled=true;
//启用“开始”按钮。
startAsyncButton.Enabled=true;
//禁用“取消”按钮。
cancelAsyncButton.Enabled=false;
}
//此事件处理程序更新进度栏。
私有void backgroundWorker1\u ProgressChanged(对象发送方,
ProgressChangedEventArgs(e)
{
这是我的进步
public static async Task<bool> RunDatabaseCommandsSmo(SqlConnection connectionString, string scriptText, string scriptName)
{
Helper.UpdateProgressLabel(scriptName);
bool isSuccess = false;
try
{
ServerConnection srvCon = new ServerConnection(connectionString);
Server server = new Server(srvCon);
//script = scriptText;
if (scriptName.Contains("Creating Database")||scriptName.Contains("Building objects"))
{
try
{
isSuccess = await Task.Run(() => RunCommand(scriptText, server))
.ConfigureAwait(continueOnCapturedContext:false);
return isSuccess;
}
catch(Exception ex)
{
log.Error("Cannot create database");
log.Error(ex.StackTrace.ToString());
return false;
}
}
else
{
try
{
//server.ConnectionContext.ExecuteNonQuery(scriptText);
isSuccess = await Task.Run(() => RunTransaction(scriptText, server, srvCon))
.ConfigureAwait(continueOnCapturedContext: false);
return isSuccess;
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.ToString());
log.Error(ex.StackTrace.ToString());
return false;
}
}
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.StackTrace.ToString());
return false;
}
}
static bool RunCommand(string script, Server server)
{
try
{
server.ConnectionContext.ExecuteNonQuery(script);
return true;
}
catch(Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
static bool RunTransaction(string script, Server server, ServerConnection srvCon)
{
try
{
srvCon.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
srvCon.CommitTransaction();
return true;
}
catch (Exception ex)
{
srvCon.RollBackTransaction();
log.Error(ex.ToString());
return false;
}
}
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace BackgroundWorkerExample
{
public class FibonacciForm : System.Windows.Forms.Form
{
private int numberToCompute = 0;
private int highestPercentageReached = 0;
private System.Windows.Forms.NumericUpDown numericUpDown1;
private System.Windows.Forms.Button startAsyncButton;
private System.Windows.Forms.Button cancelAsyncButton;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label resultLabel;
private System.ComponentModel.BackgroundWorker backgroundWorker1;
public FibonacciForm()
{
InitializeComponent();
InitializeBackgroundWorker();
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
private void startAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Reset the text in the result label.
resultLabel.Text = String.Empty;
// Disable the UpDown control until
// the asynchronous operation is done.
this.numericUpDown1.Enabled = false;
// Disable the Start button until
// the asynchronous operation is done.
this.startAsyncButton.Enabled = false;
// Enable the Cancel button while
// the asynchronous operation runs.
this.cancelAsyncButton.Enabled = true;
// Get the value from the UpDown control.
numberToCompute = (int)numericUpDown1.Value;
// Reset the variable for percentage tracking.
highestPercentageReached = 0;
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(numberToCompute);
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
// Disable the Cancel button.
cancelAsyncButton.Enabled = false;
}
// This event handler is where the actual,
// potentially time-consuming work is done.
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci((int)e.Argument, worker, e);
}
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
resultLabel.Text = "Canceled";
}
else
{
// Finally, handle the case where the operation
// succeeded.
resultLabel.Text = e.Result.ToString();
}
// Enable the UpDown control.
this.numericUpDown1.Enabled = true;
// Enable the Start button.
startAsyncButton.Enabled = true;
// Disable the Cancel button.
cancelAsyncButton.Enabled = false;
}
// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
// This is the method that does the actual work. For this
// example, it computes a Fibonacci number and
// reports progress as it does its work.
long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
{
// The parameter n must be >= 0 and <= 91.
// Fib(n), with n > 91, overflows a long.
if ((n < 0) || (n > 91))
{
throw new ArgumentException(
"value must be >= 0 and <= 91", "n");
}
long result = 0;
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
if (n < 2)
{
result = 1;
}
else
{
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
// Report progress as a percentage of the total task.
int percentComplete =
(int)((float)n / (float)numberToCompute * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
worker.ReportProgress(percentComplete);
}
}
return result;
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
this.startAsyncButton = new System.Windows.Forms.Button();
this.cancelAsyncButton = new System.Windows.Forms.Button();
this.resultLabel = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.SuspendLayout();
//
// numericUpDown1
//
this.numericUpDown1.Location = new System.Drawing.Point(16, 16);
this.numericUpDown1.Maximum = new System.Decimal(new int[] {
91,
0,
0,
0});
this.numericUpDown1.Minimum = new System.Decimal(new int[] {
1,
0,
0,
0});
this.numericUpDown1.Name = "numericUpDown1";
this.numericUpDown1.Size = new System.Drawing.Size(80, 20);
this.numericUpDown1.TabIndex = 0;
this.numericUpDown1.Value = new System.Decimal(new int[] {
1,
0,
0,
0});
//
// startAsyncButton
//
this.startAsyncButton.Location = new System.Drawing.Point(16, 72);
this.startAsyncButton.Name = "startAsyncButton";
this.startAsyncButton.Size = new System.Drawing.Size(120, 23);
this.startAsyncButton.TabIndex = 1;
this.startAsyncButton.Text = "Start Async";
this.startAsyncButton.Click += new System.EventHandler(this.startAsyncButton_Click);
//
// cancelAsyncButton
//
this.cancelAsyncButton.Enabled = false;
this.cancelAsyncButton.Location = new System.Drawing.Point(153, 72);
this.cancelAsyncButton.Name = "cancelAsyncButton";
this.cancelAsyncButton.Size = new System.Drawing.Size(119, 23);
this.cancelAsyncButton.TabIndex = 2;
this.cancelAsyncButton.Text = "Cancel Async";
this.cancelAsyncButton.Click += new System.EventHandler(this.cancelAsyncButton_Click);
//
// resultLabel
//
this.resultLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.resultLabel.Location = new System.Drawing.Point(112, 16);
this.resultLabel.Name = "resultLabel";
this.resultLabel.Size = new System.Drawing.Size(160, 23);
this.resultLabel.TabIndex = 3;
this.resultLabel.Text = "(no result)";
this.resultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(18, 48);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(256, 8);
this.progressBar1.Step = 2;
this.progressBar1.TabIndex = 4;
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
//
// FibonacciForm
//
this.ClientSize = new System.Drawing.Size(292, 118);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.resultLabel);
this.Controls.Add(this.cancelAsyncButton);
this.Controls.Add(this.startAsyncButton);
this.Controls.Add(this.numericUpDown1);
this.Name = "FibonacciForm";
this.Text = "Fibonacci Calculator";
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new FibonacciForm());
}
}
}