BeginInvoke()偶尔会中断(System.InvalidOperationException),读取器关闭时无效尝试调用Read

时间:2012-10-26 21:27:49

标签: c# winforms sqldatasource dataadapter begininvoke

在以下代码段中,从非UI线程调用AddRow()

  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();

    }
}

在必须从非UI线程调用AddRow()之前,我已直接调用UpdateGridView()并且它完美无缺。但现在不再保证从UI线程调用AddRow(),因此我用dataGridView1.BeginInvoke()替换了直接调用。

我一做到这一点,我的基于表单的应用程序就开始每System.InvalidOperationExceptionAddRow()次调用mSqlDataAdapter.Fill(mDataSet);,打破BeginInvoke()声明( )带有以下消息:

  

读取器关闭时调用Read的尝试无效

我的问题是为什么?

  1. 什么读者? DataAdapter的? SqlConnection的? DataGridView的数据源?
  2. 我正在使用mSqlConnection的 Open() Close()来处理周围的{{1}},我甚至打开mSqlConnection(再次!)如果它不是'打开,那我怎么会收到这个“关闭”错误?
  3. 解决此问题的正确方法是什么? (即从非UI线程更新DataGridView)

2 个答案:

答案 0 :(得分:2)

问题当然是由于竞争条件。

从UpdateGridView中删除这两行,因为它不是关闭连接的正确位置。

 if (mSqlConnection.State == ConnectionState.Open)
    mSqlConnection.Close();

使用IAsyncResult检索等待句柄并等待线程完成GridUpdate。

 IAsyncResult Result = dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView));
 Result.AsyncWaitHandle.WaitOne();
 mSqlConnection.Close();

答案 1 :(得分:0)

我认为你遇到了种族问题,请在你的帖子中查看这部分代码:

 dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView)); 
 mConnection.Close();

当这样的东西对于控件来说方便时,BeginInvoke将运行委托。所以你从线程中调用它并且它启动你的UI线程可以持续很长时间(你从表中得到的东西)并且在获取数据时,调用关闭连接在单独的线程中关闭它。你提到它只是偶尔发生,在大多数情况下,线程将关闭连接,你将在UpdateGridView()方法中重新打开它,但有时,你会在线程关闭连接之前开始填充数据集。您应该同步打开和关闭连接。

切换这两个应解决您的问题,但每次都会建立两个连接:

mConnection.Close();
dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView));