0

The following code is for reading from an Access DB and transferring to a SQL DB.

Can anyone tell me why this code just hangs after it is run ?

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

I have a label that should change telling me the row that is being processed but the Form just sits and becomes unresponsive once I have clicked the button.

Everything works, its just the form that freezes.

I'm open to any and all suggestions to improve the code also. I've noticed people using:

using (SqlConnection sqlCon = new SqlConnection(connectionString)) 

for example. Never really understood this. Is it better ? I have seen it used for SqlCommand objects too.

Any way this code can be improved, lets hear it.

Thanks in advance.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I'd suggest to use tasks to prevent the UI from freezing. https://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx – BytesOfMetal Mar 09 '17 at 16:18
  • This is a good time to look into [async/await](https://msdn.microsoft.com/en-us/library/mt674882.aspx). Also, you could clean your code up a bit by using `using` statements – maccettura Mar 09 '17 at 16:19
  • in line with previous comments, you could also try threading. – vipersassassin Mar 09 '17 at 16:26

1 Answers1

2
  1. You should always use using statements on anything that implements IDisposable, it ensures the resource is released even in the event of an Exception. In this case it ensures your DB Connections are closed.
  2. Put your Ado.Net types in the scope of the method, not the class. Do not worry about trying to reuse connection objects, it is almost always a bad idea and most RDBMs like Sql Server handle pooling of connections so you do not have to.
  3. Your screen can appear to hang if this takes a while. The best solution is to make your event handler (button click for example) mark with async. What the signature looks like can depend on the UI (winforms, wpf, something else) but the idea is to offload the work from the main message window. As it is not immediately clear from what and where this is being called I did not update the code to reflect this.
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); 
}

Edit with Task and async/await

In order to aleviate the freezing window you can take advantage of several async methods provided on the ado.net types. When used with async/await when an await is encountered execution is suspended and control returned to the main message window. Here is that same code but updated so that it takes advantage of the async methods. It also illustrates how you call it from a button click method.

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); 
  }
}
Igor
  • 60,821
  • 10
  • 100
  • 175
  • So all of these using statements means I no longer have to bother with con.close, con.dispose etc ? Should I then do the same for my sqlCmd object too ? –  Mar 09 '17 at 16:39
  • @SafetyFish - Correct. I missed the command instance but I updated the code to reflect that change. Disposing of `SqlCommand` is not considered necessary in most cases but it is always good practice to dispose of all types that implement `IDisposable`. The `using` block does that for you, it ensures that when the scope is left the type is disposed of. – Igor Mar 09 '17 at 16:42
  • @SafetyFish - for offloading the method see this previous answer: http://stackoverflow.com/a/1216799/1260204 and also this one http://stackoverflow.com/a/25456246/1260204 – Igor Mar 09 '17 at 16:44
  • Very interesting. Thanks so much –  Mar 09 '17 at 16:49
  • @SafetyFish - if this answers your question(s) please consider marking it as the answer (see [How to accept SO answers](http://meta.stackexchange.com/a/5235)). – Igor Mar 09 '17 at 17:03
  • 1
    If the code in the answer is supposed to be put into an async method, he either needs to switch to async methods internally, or (probably) use invoke to set that label. – Lasse V. Karlsen Mar 09 '17 at 17:38
  • @SafetyFish - See the update, this is another way to handle the freezing window symptom using async versions of those same method calls and async/await keywords to suspend execution until a result is returned from an external resource. As you saw in the other links I included in the comments you could also just wrap the call up in a background worker process or start it with Task.Run. – Igor Mar 09 '17 at 18:15
  • Brilliant. Learning a lot through this website. Excellent community. thanks, –  Mar 10 '17 at 11:17