【问题标题】:C# win forms app hangsC# win 表单应用程序挂起
【发布时间】:2017-03-09 16:16:21
【问题描述】:

以下代码用于从 Access DB 读取并传输到 SQL DB。

谁能告诉我为什么这段代码在运行后就挂了?

private void readWriteRefuseDay()
{
    OleDbDataReader dr;
    oCon = new OleDbConnection(oConStr);
    sqlCon = new SqlConnection(sqlConStr);
    oQuery = "SELECT UPRN, RefuseDay, RefuseWeek FROM RefuseDay";
    sqlQuery = "INSERT INTO Ref_RefuseDay (UPRN, RefuseDay, RefuseWeek) VALUES (@UPRN, @RefuseDay, @RefuseWeek)";
    oCmd = new OleDbCommand(oQuery, oCon);

    string sUPRN;
    string sRefuseDay;
    Int32 iRefuseWeek;

    try
    {
        oCon.Open();
        sqlCon.Open();
        count = 0;
        lblProcessing.Text = count.ToString();

        dr = oCmd.ExecuteReader();

        while (dr.Read())
        {
            lblProcessing.Text = "Processing: RefuseDay " + count.ToString();
            sUPRN = dr.GetString(0);
            sRefuseDay = dr.GetString(1);
            iRefuseWeek = dr.GetInt32(2);

            sqlCmd = new SqlCommand(sqlQuery, sqlCon);
            sqlCmd.Parameters.AddWithValue("@UPRN", sUPRN);
            sqlCmd.Parameters.AddWithValue("@RefuseDay", sRefuseDay);
            sqlCmd.Parameters.AddWithValue("@RefuseWeek", iRefuseWeek);
            sqlCmd.ExecuteNonQuery();

            count++;
        }

        dr.Close();

        oCon.Close();
        oCmd.Dispose();
        oCon.Dispose();

        sqlCon.Close();
        sqlCmd.Dispose();
        sqlCon.Dispose();
    }
    catch (Exception ex) { MessageBox.Show(ex.Message); }

我有一个应该更改的标签,告诉我正在处理的行,但是一旦我点击了按钮,表单就停在那里并且变得无响应。

一切正常,只是冻结的形式。

我也愿意接受任何和所有改进代码的建议。我注意到人们在使用:

using (SqlConnection sqlCon = new SqlConnection(connectionString)) 

例如。从来没有真正明白这一点。好点吗 ?我也看到它用于 SqlCommand 对象。

这段代码有什么可以改进的,让我们来听听。

提前致谢。

【问题讨论】:

标签: c# sql visual-studio ms-access


【解决方案1】:
  1. 您应该始终在任何实现IDisposable 的东西上使用using 语句,它确保即使在发生异常时也能释放资源。在这种情况下,它可以确保您的数据库连接已关闭。
  2. 将您的 Ado.Net 类型放在方法的范围内,而不是类。不要担心尝试重用连接对象,这几乎总是一个坏主意,而且大多数 RDBM(如 Sql Server)都会处理连接池,因此您不必这样做。
  3. 如果这需要一段时间,您的屏幕可能会出现挂起。最好的解决方案是使您的事件处理程序(例如按钮单击)标记为异步。签名的外观可能取决于 UI(winforms、wpf 等),但其想法是从主消息窗口卸载工作。由于尚不清楚调用的内容和位置,因此我没有更新代码以反映这一点。
const string oQuery = "SELECT UPRN, RefuseDay, RefuseWeek FROM RefuseDay";
const string sqlQuery = "INSERT INTO Ref_RefuseDay (UPRN, RefuseDay, RefuseWeek) VALUES (@UPRN, @RefuseDay, @RefuseWeek)";
try
{
    using(var oCon = new OleDbConnection(oConStr))
    using(var sqlCon = new SqlConnection(sqlConStr))
    using(var oCmd = new OleDbCommand(oQuery, oCon))
    {
        oCon.Open();
        sqlCon.Open();
        count = 0;
        lblProcessing.Text = count.ToString();

        using(var dr = oCmd.ExecuteReader())
        {
            while (dr.Read())
            {
                lblProcessing.Text = "Processing: RefuseDay " + count.ToString();
                var sUPRN = dr.GetString(0);
                var sRefuseDay = dr.GetString(1);
                var iRefuseWeek = dr.GetInt32(2);

                using(var sqlCmd = new SqlCommand(sqlQuery, sqlCon))
                {
                   sqlCmd.Parameters.AddWithValue("@UPRN", sUPRN);
                   sqlCmd.Parameters.AddWithValue("@RefuseDay", sRefuseDay);
                   sqlCmd.Parameters.AddWithValue("@RefuseWeek", iRefuseWeek);
                   sqlCmd.ExecuteNonQuery();
                }
                count++;
            }
        }
    }
}
catch (Exception ex) { 
  MessageBox.Show(ex.Message); 
}

使用 Task 和 async/await 编辑

为了缓解冻结窗口,您可以利用 ado.net 类型上提供的几种异步方法。当遇到等待时与 async/await 一起使用时,执行将暂停,并将控制返回到主消息窗口。这是相同的代码,但已更新,以便利用异步方法。它还说明了如何从按钮单击方法调用它。

private async void button_Click(object sender, EventArgs e)
{
    await readWriteRefuseDayAsync();
}

private async Task readWriteRefuseDayAsync() {
  const string oQuery = "SELECT UPRN, RefuseDay, RefuseWeek FROM RefuseDay";
  const string sqlQuery = "INSERT INTO Ref_RefuseDay (UPRN, RefuseDay, RefuseWeek) VALUES (@UPRN, @RefuseDay, @RefuseWeek)";
  try {
    using(var oCon = new OleDbConnection(oConStr))
    using(var sqlCon = new SqlConnection(sqlConStr))
    using(var oCmd = new OleDbCommand(oQuery, oCon))
    {
        await oCon.OpenAsync();
        await sqlCon.OpenAsync();
        count = 0;
        lblProcessing.Text = count.ToString();

        using(var dr = await oCmd.ExecuteReaderAsync())
        {
            while (await dr.ReadAsync())
            {
                lblProcessing.Text = "Processing: RefuseDay " + count.ToString();

                var sUPRN = dr.GetString(0);
                var sRefuseDay = dr.GetString(1);
                var iRefuseWeek = dr.GetInt32(2);

                using(var sqlCmd = new SqlCommand(sqlQuery, sqlCon))
                {
                   sqlCmd.Parameters.AddWithValue("@UPRN", sUPRN);
                   sqlCmd.Parameters.AddWithValue("@RefuseDay", sRefuseDay);
                   sqlCmd.Parameters.AddWithValue("@RefuseWeek", iRefuseWeek);
                   await sqlCmd.ExecuteNonQueryAsync();
                }
                count++;
            }
        }
    }
  }
  catch (Exception ex) {
    MessageBox.Show(ex.Message); 
  }
}

【讨论】:

  • 所以所有这些 using 语句意味着我不再需要为 con.close、con.dispose 等而烦恼?然后我应该对我的 sqlCmd 对象做同样的事情吗?
  • @SafetyFish - 正确。我错过了命令实例,但我更新了代码以反映该更改。在大多数情况下,处理SqlCommand 不是必需的,但处理所有实现IDisposable 的类型始终是一种好习惯。 using 块为您执行此操作,它确保在离开范围时,类型被释放。
  • @SafetyFish - 要卸载该方法,请参阅之前的答案:stackoverflow.com/a/1216799/1260204 和这个stackoverflow.com/a/25456246/1260204
  • 非常有趣。非常感谢
  • 如果答案中的代码应该放入异步方法中,他要么需要在内部切换到异步方法,要么(可能)使用调用来设置该标签。
猜你喜欢
  • 2011-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-07-04
  • 2011-02-10
相关资源
最近更新 更多