I'm having an issue with a form that uses a background worker thread to fetch data and update a datagrid.
The use case is a list with progress bars that shows how much tables have loaded in a database.
It goes in the following order,
- FormA instantiates FormB
- FormB kicks of a background worker that loops to fetch data and update a datagrid.datasource.
It works nicely sometimes but others the FormB.ShowDialog()
comes back with a null reference exception. I can't understand why.
Here's the calling form code:
private void button1_Click(object sender, EventArgs e)
{
using (var f = new FormFactProgress(_dwConnectionString, _sourceConnectionString, _etlConnectionString))
{
f.ShowDialog(); \\ THIS IS WHERE THE NULL REFERENCE EXCEPTION ALWAYS HAPPENS!
labelLoadWarning.Visible = false;
groupBoxLoadFacts.Enabled = false;
buttonLoadFacts.BackColor = Color.FromArgb(128, 255, 128);
buttonLoadFacts.Text = "Loaded";
if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString) && dynamicWarehouseCatalog.DimsLoaded(_dwConnectionString))
{
groupBoxSync.Enabled = true;
}
}
}
The child form (It's the second backgroundworker that does the datagrid update):
private void buttonStart_Click(object sender, EventArgs e)
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
backgroundWorker2.DoWork += new DoWorkEventHandler(backgroundWorker2_DoWork);
if (!backgroundWorker2.IsBusy)
{
backgroundWorker2.RunWorkerAsync();
}
buttonStart.Enabled = false;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BackgroundWorker worker = (BackgroundWorker)sender;
sSISDBCatalog.StartFactLoad(_etlConnectionString);
while (isLoading)
{
System.Threading.Thread.Sleep(1000);
if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString))
{
if (dynamicWarehouseCatalog.AllFactsLoaded(_dwConnectionString))
{
isLoading = false;
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(
"Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Control.CheckForIllegalCrossThreadCalls = false;
BackgroundWorker worker = (BackgroundWorker)sender;
var dw = DwData(_dwConnectionString);
var source = SourceData(_sourceConnectionString);
dataGridView1.DataSource = GetProgress(dw, source);
while (isLoading)
{
System.Threading.Thread.Sleep(1000);
dw = DwData(_dwConnectionString);
dataGridView1.DataSource = GetProgress(dw, source);
dataGridView1.Refresh();
}
MessageBox.Show(
"Your data warehouse facts are loaded!", "Facts Loaded",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
catch (Exception ex)
{
MessageBox.Show(
"Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
}
Invoke
orbackgroundWorker.ReportProgress()
etc. instead.CheckForIllegalCrossThreadCalls = false
is a very bad idea! For the exception: check the stack trace exactly,f
cannot be null, or see: stackoverflow.com/questions/4660142/… – René VogtDoWork
. You have to handle theRunWorkerCompleted
event and modify the UI there. It's a lot easier to useasync/await
by this point. Combining two async operations with BGW is a lot harder too – Panagiotis Kanavos