In following code snippet, AddRow() is called from a non-UI thread:
public partial class Form1 : Form
{
public delegate void InvokeDelegate();
...
SqlConnection mSqlConnection = new SqlConnection("Data Source=" + Environment.MachineName + "\\SQLEXPRESS; Initial Catalog=orderDB; Integrated Security=TRUE; MultipleActiveResultSets=True;");
DataSet mDataSet = new DataSet();
SqlDataAdapter mSqlDataAdapter = new SqlDataAdapter();
...
private void UpdateGridView()
{
if (mSqlConnection.State == ConnectionState.Closed)
mSqlConnection.Open();
mSqlDataAdapter.SelectCommand = new SqlCommand("SELECT * FROM customerTable", mSqlConnection);
mDataSet.Clear();
mSqlDataAdapter.Fill(mDataSet);
dataGridView1.DataSource = mDataSet.Tables[0];
if (mSqlConnection.State == ConnectionState.Open)
mSqlConnection.Close();
}
public void AddRow(int field1, int field2, int field3)
{
mSqlDataAdapter.InsertCommand = new SqlCommand("INSERT INTO customerTable VALUES(@field1, @field2, @field3)", mSqlConnection);
mSqlDataAdapter.InsertCommand.Parameters.Add("@field1", SqlDbType.Int).Value = field1;
mSqlDataAdapter.InsertCommand.Parameters.Add("@field2", SqlDbType.Int).Value = field2;
mSqlDataAdapter.InsertCommand.Parameters.Add("@field3", SqlDbType.Int).Value = field3;
mSqlConnection.Open();
mSqlDataAdapter.InsertCommand.ExecuteNonQuery();
dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView)); // UpdateGridView() won't work from a non-UI thread
mSqlConnection.Close();
}
}
Before having to call AddRow() from a non-UI thread, I had UpdateGridView() being called directly and it worked flawlessly. But now AddRow() is no longer guaranteed to be called from a UI thread and so I replaced the direct call with dataGridView1.BeginInvoke().
As soon as I did that, my form-based application started throwing a System.InvalidOperationException every several AddRow() calls, breaking on the mSqlDataAdapter.Fill(mDataSet); statement (!) with the following message:
Invalid attempt to call Read when reader is closed
My question is why?
- What reader? the DataAdapter’s? The SqlConnection’s? The DataGridView’s data source?
- I am taking care of surrounding
BeginInvoke()with mSqlConnection’s Open() and Close() and I even open mSqlConnection (again!) if it isn’t open, so how come I’m receiving this “closed” error? - What is the correct way to solve this problem? (i.e. updating DataGridView from a non-UI thread)
The issue is certainly due to the race condition.
Remove these two lines from UpdateGridView,as it is not the right place to close your connection.
use IAsyncResult to retrieve a waithandle and wait for the thread to complete the GridUpdate.