0

I'm working with two stored procedures in an ASP.NET button function. While I get an error message based on the results that the invoice number is already dispatched from the other stored procedure, it still moves to the other stored procedure and executes it.

If the user gets this error message:

This invoice num was already dispatched!

then it shouldn't move on to this aspect of the function

protected void Button2_Click(object sender, EventArgs e)
{
    try
    {
        for (int i = GridView2.Rows.Count - 1; i >= 0; i--)
        {
            var row = GridView2.Rows[i];

            CheckBox chk = row.FindControl("chkInvoice") as CheckBox;
            //CheckBox chk = (CheckBox)GridView2.Rows[i].Cells[0].FindControl("CheckBox3");

            if (chk != null && chk.Checked)
            {
                string strSQLconstring = System.Configuration.ConfigurationManager.ConnectionStrings["TWCL_OPERATIONSConnectionString"].ToString();

                using (SqlConnection objConnection = new SqlConnection(strSQLconstring))
                {
                    objConnection.Open();

                    using (SqlTransaction transaction = objConnection.BeginTransaction())
                    {
                        string SID = GridView2.Rows[i].Cells[3].Text.Trim();
                        SqlDataReader myReader = null;

                        using (SqlCommand command = new SqlCommand("PP_SelectStatus", objConnection, transaction))
                        {
                            command.CommandType = CommandType.StoredProcedure;

                            command.Parameters.AddWithValue("@invoiceNum", SID);
                            command.Parameters.AddWithValue("@custPONum", GridView2.Rows[i].Cells[4].Text.Trim());

                            myReader = command.ExecuteReader();

                            if (myReader.Read())
                            {
                                string invoice1 = (myReader["status"].ToString());
                                if (invoice1 == "0")
                                {
                                    ClientScript.RegisterClientScriptBlock(this.GetType(), "alert", "alert('This invoice num was already dispatched!')", true);
                                }

                                myReader.Close();
                            }
                        }
                        else if (invoice1=="1")
                        {
                            using (SqlCommand cmd = new SqlCommand("PP_RemoveInvoice", objConnection, transaction))
                            {
                                cmd.CommandType = CommandType.StoredProcedure;

                            cmd.Parameters.AddWithValue("@loadSheetNum", txtDispatchNum.Text);
                            cmd.Parameters.AddWithValue("@invoiceNum", SID);
                            cmd.Parameters.AddWithValue("@removeUser", lblUsername.Text.Replace("Welcome", ""));

                            **int a = cmd.ExecuteNonQuery();**

                            cmd.Dispose();

                            if (a > 0)
                            {
                                dt.Rows.RemoveAt(i);

                                ////Read invoice qty from grid view 2
                                string invoice = GridView2.Rows[i].Cells[5].Text.ToString();
                                decimal invoiceTotal = Convert.ToDecimal(txtInvoiceTotal.Text) - Convert.ToDecimal(invoice);
                                txtInvoiceTotal.Text = invoiceTotal.ToString();

                                ////Read invoice weight from grid view 2
                                string weight = GridView2.Rows[i].Cells[6].Text.ToString();
                                decimal invoiceWeight = Convert.ToDecimal(txtQtyWeight.Text) - Convert.ToDecimal(weight);
                                txtQtyWeight.Text = invoiceWeight.ToString();
                                lblError.ForeColor = Color.Green;

                                lblError.Text = "Selected record(s) successfully updated";
                            }
                            else
                            {
                                lblError.ForeColor = Color.Red;
                                lblError.Text = " Record has not yet been recorded";
                            }
                        }

                        //objConnection.Close();
                        transaction.Commit();
                    }
                  }
                }

                //Button2.Visible = false;
                //showData();
                GridView2.DataSource = dt;
                GridView2.DataBind();

                txtInvoiceCount.Text = dt.Rows.Count.ToString();
            }
        }
    }
    catch (Exception ex)
    {
        if (ex.Message.StartsWith("Violation of PRIMARY KEY constraint"))
        {
            lblError.ForeColor = Color.Red;
            lblError.Text = " This invoice number was remove from dispatch sheet before!!";
        }
        else
        {
            // re-throw the error if you haven't handled it
            lblError.Text = ex.Message;
            throw;
        }
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sue
  • 47
  • 8
  • Don't you need a `if` statement (or some other flow of control structure) that determines whether the second stored procedure is executed? Where is it? – John Wu Jul 24 '18 at 23:45
  • @JohnWu, i've added an else if but it keeping this error There is already an open DataReader associated with this Command which must be closed first. – Sue Jul 25 '18 at 00:00
  • So close it. What is the question? – John Wu Jul 25 '18 at 00:05
  • @JohnWu; I did but still throws the error unless it show be somewhere else – Sue Jul 25 '18 at 00:08
  • @JohnWu; I understand the error but I'm not sure what I'm doing incorrectly – Sue Jul 25 '18 at 00:27
  • Please edit your question to update your example, and highlight the part of the code that throws the exception. – John Wu Jul 25 '18 at 00:32
  • Question was updated. Throws error here int a = cmd.ExecuteNonQuery(); – Sue Jul 25 '18 at 00:36
  • @Sue: your else if (invoice1=="1") is currently with if (chk != null && chk.Checked) – Gauravsa Jul 25 '18 at 00:43
  • You are closing the reader only when `myReader.Read()` returns true. You need to close it regardless of the result. – John Wu Jul 25 '18 at 00:49

2 Answers2

1

You have a very, very simple logic error, but it is incredibly hard to see because your code is such a mess. Therefore, my answer is:

REFACTOR REFACTOR REFACTOR

It is important to get into the habit of writing short functions and controlling their inputs and outputs. If you don't do this, even a fairly trivial operation like this one gets very confusing and error-prone.

Here is an example of how to organize things. We remove most of the code from the click handler:

protected void DeleteButton_Click(object sender, EventArgs e)
{
    for (int i = GridView2.Rows.Count - 1; i >= 0; i--)
    {
        var row = GridView2.Rows[i];
        if (IsChecked(row))
        {
            var result = ProcessRow(row, i);
            DisplayResult(i, result);
        }
    }
}

Firstly, notice it has a meaningful name. These become very important as your application grows. Also, look how short it is! Where did all the code go? Well, it went into two separate methods, which are now short enough for us to view on one page-- a common requirement that IT organizations impose on their programmers, to avoid spaghetti code.

protected TransactionResult ProcessRow(GridViewRow row, int index)
{
    var SID = GridView2.Rows[index].Cells[3].Text.Trim();
    var custPONum = GridView2.Rows[index].Cells[4].Text.Trim();
    var loadSheetNum = txtDispatchNum.Text;
    var removeUser = lblUsername.Text.Replace("Welcome", "");

    return ExecuteInvoiceTransaction(SID, custPONum, loadSheetNum, removeUser);
}

And

public void DisplayResult(int rowIndex, TransactionResult result)
{
    switch result
    {
        case TransactionResult.Success:
            dt.Rows.RemoveAt(rowIndex);
            DisplayTotals(rowIndex);
            DisplaySuccess("Selected record(s) successfully updated");
            break;

        case TransactionResult.AlreadyDispatched;
            ClientScript.RegisterClientScriptBlock(this.GetType(), "alert", "alert('This invoice num was already dispatched!')", true);
            break;

        case TransactionResult.RecordNotRecorded;
            DisplayError("Record has not yet been recorded");
            break;

        case TransactionResult.AlreadyRemoved:
            DisplayError("This invoice number was remove from dispatch sheet before!!");
            break;
    }
}

These methods in turn call a variety of helper methods, each of which does one thing and one thing only. This could be referred to as separation of concerns, which is really important for structured code.

Here's the rest of the methods:

enum TransactionResult
{
    Success,
    AlreadyDispatched,
    RecordNotRecorded,
    AlreadyRemoved
}

private bool ExecuteSelectStatus(SqlConnection connection, SqlTransaction transaction, string invoiceNum, string custPONum)
{
    using (SqlCommand command = new SqlCommand("PP_SelectStatus", objConnection, transaction))
    {
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.AddWithValue("@invoiceNum", invoiceNum);
        command.Parameters.AddWithValue("@custPONum", custPONum);
        using (var myReader = command.ExecuteReader())
        {
            if (myReader.Read())
            {
                string invoice1 = (myReader["status"].ToString());
                if (invoice1 == "0")
                {
                    return false;
                }
            }
        }
        return true;
    }
}

private int ExecuteRemoveInvoice(SqlConnection objConnection, SqlTransaction transaction, string loadSheetNum, string invoiceNum, string removeUser)
{
    try
    {
        using (SqlCommand cmd = new SqlCommand("PP_RemoveInvoice", objConnection, transaction))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@loadSheetNum", loadSheetNum);
            cmd.Parameters.AddWithValue("@invoiceNum", invoiceNum);
            cmd.Parameters.AddWithValue("@removeUser", removeUser);
            return cmd.ExecuteNonQuery();
        }
    }
    catch (SqlException ex)
    {
        if (ex.Number == 2627) //Primary key violation
        {
            return -1;
        }
    }
}

protected TransactionResult ExecuteInvoiceTransaction(string invoiceNum, string custPONum, string loadSheetNum, string removeUser)
{
    var strSQLconstring = System.Configuration.ConfigurationManager.ConnectionStrings["TWCL_OPERATIONSConnectionString"].ToString();

    using (SqlConnection objConnection = new SqlConnection(strSQLconstring))
    {
        objConnection.Open();

        using (SqlTransaction transaction = objConnection.BeginTransaction())
        {
            var ok = ExecuteSelectStatus(objConnection, transaction, invoiceNum, custPONum);
            if (!ok) return TransactionResult.AlreadyDispatched;

            var a = ExecuteRemoveInvoice(objConnection, transaction, loadSheetNum, invoiceNum, removeUser);
            switch a
            {
                case -1: 
                    return TransactionResult.AlreadyRemoved;
                case 0:
                    return TransactionResult.RecordNotRecorded;
                default:
                    transaction.Commit();
                    return TransactionResult.Success;
            }
        }
    }
}

public void DisplayTotals(int i)
{
    ////Read invoice qty from grid view 2
    string invoice = GridView2.Rows[i].Cells[5].Text;
    decimal invoiceTotal = Convert.ToDecimal(txtInvoiceTotal.Text) - Convert.ToDecimal(invoice);
    txtInvoiceTotal.Text = invoiceTotal.ToString();

    ////Read invoice weight from grid view 2
    string weight = GridView2.Rows[i].Cells[6].Text();
    decimal invoiceWeight = Convert.ToDecimal(txtQtyWeight.Text) - Convert.ToDecimal(weight);
    txtQtyWeight.Text = invoiceWeight.ToString();
}

public void DisplaySuccess(string message)
{
    lblError.ForeColor = Color.Green;
    lblError.Text = message;
}

public void DisplayError(string message)
{
    lblError.ForeColor = Color.Red;
    lblError.Text = message;
}

A few things to note:

  • You don't need to call Dispose() if you are using using.
  • You should always catch the most specific exception possible, per Microsoft's guidance. My example does this.
  • The exception handling for the primary key error is isolated into the method that calls the stored procedure. The overall business logic shouldn't have to know details about the SQL implementation. I've shown how you can identify the specific error based on this post.
  • Because there are four possible outcomes, I added an enumeration called TransactionResult so we could return the status to the caller easily.
  • Some of these methods are short-- just two lines-- and that is OK. The main reason to separate them out is to give them a meaningful name and make the code shorter and easier to read.
  • This code is much more structured but it could still be improved! In many implementations, the code that accesses the database is actually moved to a completely different layer or project.
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • I'm still receiving the same error even this; when it reaches if (invoice1 == "0") { return false; //it throws the same error } – Sue Jul 25 '18 at 13:36
  • Please post the exception message and stack trace. I suspect you are leaving something important out; there is no way comparing two strings like that could fail. – John Wu Jul 25 '18 at 14:59
  • transaction.Commit(); return TransactionResult.Success; right there giving this error There is already an open DataReader associated with this Command which must be closed first – Sue Jul 25 '18 at 15:55
  • I made a small edit Sue, to use use `using` instead of an explicit close (which is probably a [better idea](https://stackoverflow.com/questions/744051/is-it-necessary-to-manually-close-and-dispose-of-sqldatareader)). See if it works for you now. – John Wu Jul 25 '18 at 16:47
0

See if this works. Moved your if/else together:

protected void Button2_Click(object sender, EventArgs e)
    {
        try
        {
            for (int i = GridView2.Rows.Count - 1; i >= 0; i--)
            {
                var row = GridView2.Rows[i];

                CheckBox chk = row.FindControl("chkInvoice") as CheckBox;

                if (chk != null && chk.Checked)
                {
                    string strSQLconstring = System.Configuration.ConfigurationManager.ConnectionStrings["TWCL_OPERATIONSConnectionString"].ToString();

                    using (SqlConnection objConnection = new SqlConnection(strSQLconstring))
                    {
                        objConnection.Open();

                        using (SqlTransaction transaction = objConnection.BeginTransaction())
                        {
                            string SID = GridView2.Rows[i].Cells[3].Text.Trim();
                            SqlDataReader myReader = null;
                            using (SqlCommand command = new SqlCommand("PP_SelectStatus", objConnection, transaction))
                            {

                                command.CommandType = CommandType.StoredProcedure;

                                command.Parameters.AddWithValue("@invoiceNum", SID);
                                command.Parameters.AddWithValue("@custPONum", GridView2.Rows[i].Cells[4].Text.Trim());
                                myReader = command.ExecuteReader();

                                if (myReader.Read())
                                {

                                    string invoice1 = (myReader["status"].ToString());
                                    if (invoice1 == "0")
                                    {
                                        ClientScript.RegisterClientScriptBlock(this.GetType(), "alert", "alert('This invoice num was already dispatched!')", true);
                                    }

                                    else if (invoice1 == "1")
                                    {
                                        using (SqlCommand cmd = new SqlCommand("PP_RemoveInvoice", objConnection, transaction))
                                        {
                                            cmd.CommandType = CommandType.StoredProcedure;

                                            cmd.Parameters.AddWithValue("@loadSheetNum", txtDispatchNum.Text);
                                            cmd.Parameters.AddWithValue("@invoiceNum", SID);
                                            cmd.Parameters.AddWithValue("@removeUser", lblUsername.Text.Replace("Welcome", ""));

                                            int a = cmd.ExecuteNonQuery();
                                            cmd.Dispose();

                                            if (a > 0)
                                            {
                                                dt.Rows.RemoveAt(i);

                                                ////Read invoice qty from grid view 2
                                                string invoice = GridView2.Rows[i].Cells[5].Text.ToString();
                                                decimal invoiceTotal = Convert.ToDecimal(txtInvoiceTotal.Text) - Convert.ToDecimal(invoice);
                                                txtInvoiceTotal.Text = invoiceTotal.ToString();

                                                ////Read invoice weight from grid view 2
                                                string weight = GridView2.Rows[i].Cells[6].Text.ToString();
                                                decimal invoiceWeight = Convert.ToDecimal(txtQtyWeight.Text) - Convert.ToDecimal(weight);
                                                txtQtyWeight.Text = invoiceWeight.ToString();
                                                lblError.ForeColor = Color.Green;

                                                lblError.Text = "Selected record(s) successfully updated";
                                            }
                                            else
                                            {
                                                lblError.ForeColor = Color.Red;
                                                lblError.Text = " Record has not yet been recorded";
                                            }
                                        }
                                        //objConnection.Close();
                                        transaction.Commit();
                                    }


                                }
                            }

                            GridView2.DataSource = dt;
                            GridView2.DataBind();
                            txtInvoiceCount.Text = dt.Rows.Count.ToString();
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            if (ex.Message.StartsWith("Violation of PRIMARY KEY constraint"))
            {
                lblError.ForeColor = Color.Red;
                lblError.Text = " This invoice number was remove from dispatch sheet before!!";
            }
            else
            {
                // re-throw the error if you haven't handled it
                lblError.Text = ex.Message;
                throw;

            }
        }
    }

}
Gauravsa
  • 6,330
  • 2
  • 21
  • 30