Ok, here is a small test section that I have put together to illustrate my
problem:
We have an SQL query that takes 60-120 seconds to run for each employee (the
SqlCommand's CommandTimeout is set to 120 seconds and this seems to give the
queries enough time).
public static string m_fmtSql = "SELECT LOTS OF STUFF WHERE EMPLOYEE='{0}'";
public static SqlConnection m_conn; // set elsewhere
void worker_DoWork(object sender, DoWorkEventArgs e) {
List<string> Employees = (List<string>)e.Argument;
List<string> Errors = new List<string>();
List<string> Results = new List<string>();
BackgroundWorker worker = sender as BackgroundWorker;
int Count = 0;
foreach (string person in Employees) {
if (worker.CancellationPending == true) {
e.Cancel = true;
return;
}
Count++;
worker.ReportProgress(100 * Count / Employees.Count);
DataTable dt = new DataTable();
string sql = string.Format(m_fmtSql, person);
using (SqlDataAdapter da = new SqlDataAdapter(sql, m_conn)) {
try {
da.Fill(dt); // this step takes 60-120 seconds to run
} catch (SqlException err) {
Errors.Add(err.Message);
}
}
if (0 < dt.Rows.Count) {
string fmt = "{0};{1};{2};{3};{4};{5};{6}";
string var1, var2, var3, var4, var5;
int var6, var7;
// work with data, manipulate some figures, then add it to the results
string result
= string.Format(fmt, var1, var2, var3, var4, var5, var6, var7);
Results.Add(result);
}
}
e.Result = Results;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
// display error
} else if (e.Cancelled == true) {
// display that status
} else if (e.Result is List<string>) {
List<string> Results = (List<string>)e.Result;
DataGridView1.Rows.Clear();
foreach (string line in Results) {
string[] data = line.Split(new char[] { ';' });
DataGridView1.Rows.Add(data);
}
}
}
void BtnCancel_Click(object sender, EventArgs e) {
if ((WorkerThread.IsBusy == true) &&
(WorkerThread.SupportsCancellation == true)) {
WorkerThread.CancelAsync();
// Here is the point of concern:
// since the SQL call could take up to 2 full minutes to complete,
// our Users are liable to try running the report again while
// the thread is still active.
// So, the solution is to disable the "Run Report" button until
// this thread has finished:
BtnRunReport.Enabled = false;
do {
Thread.Sleep(0);
} while (WorkerThread.IsBusy == true);
BtnRunReport.Enabled = true;
// The problem is:
// This code is so busy in the loop, that the
// "RunWorkerCompleted" code does not have a chance to run.
// If I write in the ugly "Application.DoEvents();" within the
do...while loop,
// the application will stop itself, but that is a bad fix.
}
}