3

I'm using an SQLDataReader to populate a GridView (GridView1) on an asp.net page. The SQLDataReader sets itself up in the C# Codebehind, like so:

        string MySQLString;
        MySQLString = "SELECT * FROM [vw_Report_Latest_v3_1] WHERE [FKID_Contract]=@Contract";
        if ((string)Session["TSAreaString"] != "") { MySQLString = MySQLString + " AND [L1_Name]=@PA1"; }
        if ((string)Session["TSSiteString"] != "") { MySQLString = MySQLString + " AND [L2_Name]=@PA2"; }
        if ((string)Session["TSFeatureString"] != "") { MySQLString = MySQLString + " AND [L3_Name]=@PA3"; }
        if ((string)Session["TSS1"] != "") { MySQLString = MySQLString + " AND [Spare1]=@S1"; }
        if ((string)Session["TSS2"] != "") { MySQLString = MySQLString + " AND [Spare2]=@S2"; }
        if ((string)Session["TSS3"] != "") { MySQLString = MySQLString + " AND [Spare3]=@S3"; }
        if ((string)Session["TSS4"] != "") { MySQLString = MySQLString + " AND [Spare4]=@S4"; }
        if ((string)Session["TSS5"] != "") { MySQLString = MySQLString + " AND [Spare5]=@S5"; }
        if ((string)Session["TSTaskString"] != "") { MySQLString = MySQLString + " AND [Operation_Name]=@PA4"; }
        if ((string)Session["TSTeamString"] != "") { MySQLString = MySQLString + " AND [Team_Name]=@Team"; }
        //finish
        MySQLString = MySQLString + " ORDER BY [OperationOrder], [L1_Name], [L2_Name], [L3_Name], [Operation_Name], [Team_Name]";
        try
        {
            Conn.Open();
            SqlCommand Cmd = new SqlCommand(MySQLString, Conn);
            Cmd.Parameters.AddWithValue("@Contract", Convert.ToInt32(invCID.Text));
            if ((string)Session["TSAreaString"] != "") { Cmd.Parameters.AddWithValue("@PA1", (string)Session["TSAreaString"]); }
            if ((string)Session["TSSiteString"] != "") { Cmd.Parameters.AddWithValue("@PA2", (string)Session["TSSiteString"]); }
            if ((string)Session["TSFeatureString"] != "") { Cmd.Parameters.AddWithValue("@PA3", (string)Session["TSFeatureString"]); }
            if ((string)Session["TSS1"] != "") { Cmd.Parameters.AddWithValue("@S1", (string)Session["TSS1"]); }
            if ((string)Session["TSS2"] != "") { Cmd.Parameters.AddWithValue("@S2", (string)Session["TSS2"]); }
            if ((string)Session["TSS3"] != "") { Cmd.Parameters.AddWithValue("@S3", (string)Session["TSS3"]); }
            if ((string)Session["TSS4"] != "") { Cmd.Parameters.AddWithValue("@S4", (string)Session["TSS4"]); }
            if ((string)Session["TSS5"] != "") { Cmd.Parameters.AddWithValue("@S5", (string)Session["TSS5"]); }
            if ((string)Session["TSTaskString"] != "") { Cmd.Parameters.AddWithValue("@PA4", (string)Session["TSTaskString"]); }
            if ((string)Session["TSTeamString"] != "") { Cmd.Parameters.AddWithValue("@Team", (string)Session["TSTeamString"]); }
            Cmd.Connection = Conn;
            SqlDataReader reader = Cmd.ExecuteReader(CommandBehavior.CloseConnection);
            GridView1.DataSource = reader;
            GridView1.DataBind();
        }
        finally
        {
            if (Conn != null) { Conn.Close(); }
        }

This is giving me serious problems. If, for instance, we set the L1_Name (by giving TSAreaString a value) to "Town", it will bring up everything where L1_Name is "Town". Which is fine and dandy. Takes a couple of seconds because it's a big Town.

However, if we set L1_Name to "Town" AND TSS3 (in this example) to "County", then it takes much, much longer - sometimes over a minute - despite it retrieving the same amount of records, or sometimes less.

Unfortunately we have to have this in - we can't just search on "Town", we have to search through "Town" and "County" due to impositions by our client.

The View this is running from - vw_Report_Latest_v3_1 - runs absolutely fine. There are no problems with that, even using the criteria above. Both scenarios - Town, and Town AND County, take the same amount of time on the View alone through SQL Server 2008.

I'm pretty sure it's the reading/binding somehow.

6
  • 3
    Your code hurts my head to read. You should use a using statment instead of try/finally. You should also store all of your relevant parameters in an object, and store that in the Session, rather than as tons of seperate objects in the session. Finally, you should not be doing database work in the code behind, you should have a separate layer responsible for doing that. Commented Apr 10, 2015 at 13:50
  • If it's a databind issue, you could measure the time to execute the query, and the time to do the databind... But it's definitely not a databind issue.... once the data is populated it doesn't matter what was the query to get the data as long it is returning the same amount of data. Commented Apr 10, 2015 at 13:57
  • Run the query generated in SQL management studio and look at the execution plan. Commented Apr 10, 2015 at 14:03
  • Must admit I'm new to this type of SQL. However, I don't think the SQL is the answer - the View is working fine through SQL Management Studio, even with as many criteria as I care to put in, and is running mostly fine through the above Codebehind. It's just the addition of a criteria in the Codebehind version for some reason that slows it right down. Commented Apr 10, 2015 at 14:14
  • It could be an issue with AddWithValue(). Sometimes ADO.Net guesses wrong parameter types in ways that break index use. Commented Apr 10, 2015 at 14:14

3 Answers 3

1

Your SQL connection code doesn't look like it is the problem, nor the GridView. Try [on a higher scope] taking your Session variables and saving it into a Dictionary<string, object> or a Dictionary<string, string> (or something of that approach) and retrieve your values from that instead of hitting the browser's session for every value (twice).

Either that or the indexing for your view/underlying tables are non-existent or troublesome. Make sure to check those as well.

Some things to consider: 1. For what you're trying to do, make a stored procedure that doesn't involve you having to construct a SQL query yourself. You will give yourself headaches when you add complexity in the future. For example, instead of:

if ((string)Session["TSAreaString"] != "") { MySQLString = MySQLString + " AND [L1_Name]=@PA1"; }

...your stored procedure could very easily use COALESCE or ISNULL to achieve the same results.

--parameter for stored procedure
@TSAreaString nvarchar(max) = NULL


SELECT v.* 
FROM View v
WHERE v.TSAreaString = COALESCE(@TSAreaString, v.TSAreaString)

(SQL Server syntax)

If you used this approach, you could eliminate the top half of your code and do something like this in the second half:

Cmd.Parameters.AddWithValue("@Team", String.IsNullOrWhiteSpace((string)Session["TSTeamString"]) ? DBNull.Value : (string)Session["TSTeamString"]; 

However, if you going to continue using your same approach:

  1. Use StringBuilder instead of string. It is immutable and if you're worried about performance, it will perform better because of this.

  2. Wrap your connection object and your command object into a using clause for automatic disposal for the time being (long term, make your own classes to handle the different database operations).

  3. And actually this might be part of your problem, but I'm not sure because I haven't done it your way before. Instead of making the reader object the DataSource of the grid view, make a class that represents your data, and use the reader to populate a List<YourClass> of it using

    while (reader.Read()) { YourList.Add(RetrieveYourClass(reader)); }

(^ SO is not code highlighting this for some reason)

Sign up to request clarification or add additional context in comments.

1 Comment

I definitely know my code is not the best, but I'm leaning now towards indexing (and my lack thereof). I think I need to know how to index the table effectively.
1

there are several areas of improvement that you could do.

  • You should use StringBuilder instead of string = string +""; its more efficient
  • instead of != "", i would suggestion !string.IsNullOrEmpty((string)Session[""]). that way it catches null, string.empty, and ""
  • (personal preference) Don't be afraid of white space. having the if statements on the same line..its hard to read.
  • good job wrapping stuff in {} its a nightmare for the next person if you dont do that
  • Its a good idea to seperate layers (as @mason mentioned). I personally have a data layer to house the queries. has no logic. then a business layer or class layer. it houses the logic. cleaning, verifying, etc... then the code behind which passes the values into class layer.
  • I see two types of business layers. there is the true object style. data layer turns datatable then it gets loaded into the class. Look up POCO to get an idea of that. the other, which is where I started a few years ago, is a layer system. each method is self contained and just passes stuff to the data layer and if it is a select, then returns a data table.
  • extract out the code that contacts the database. (See my code at the bottom)

you mentioned a view. check your indexing. since this is mysql...i dont know hwo to do this but when using Microsoft SQL, you can use whats called estimated execution path. so you would put the select statement in MSSMS software, and instead of executing, you can click the "Display estimated execution path" button which will make suggestions for indexing etc.

here is how your data layer could look and how it could use the connector (which is the next code block)

private MySQLConnector _MySQL = null;
protected MySQLConnector MySQL
{
   get
   {
      if (_MySQL == null)
      {
         _MySQL = new MySQLConnector();
      }
      return _MySQL;
   }
}

public void Update(int programId, int LocationId, string Name, string modifiedBy)
   {
   List<MySqlParameter> parameterList = new List<MySqlParameter>();

   parameterList.Add(new MySqlParameter("ProgramID", programId));
   parameterList.Add(new MySqlParameter("LocationId", LocationId));
   parameterList.Add(new MySqlParameter("Name", Name));
   if (!string.IsNullOrEmpty(modifiedBy))
   {
      parameterList.Add(new MySqlParameter("ModifiedBy", modifiedBy));
   }
   else
   {
      parameterList.Add(new MySqlParameter("ModifiedBy", DBNull.Value));
   }

   const string TheSql = @"
            UPDATE ProgramLocation
            SET
           Name = @Name,
               ModifiedOn = GETDATE(),
               ModifiedBy = @ModifiedBy
            WHERE
        ProgramID = @ProgramID
        AND LocationId = @LocationId";

   MySQL.ExecuteNonQuerySql(TheSql, parameterList);
}

here is the code that contacts the database. its a bit dated and you might need to change it the package your using to contact the MySQL Database.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Configuration;
using System.Reflection;
using MySql.Data.MySqlClient;

namespace DEFINETHENameSpace
{
    public class MySQLConnector
    {
        private string connString = null;

        public string TheConnectionString
        {
            get
            {
                if (string.IsNullOrEmpty(connString))
                {
                    //  connString = ConfigurationManager.ConnectionStrings["MySQLConnection"].ConnectionString; 
                    throw new Exception("No Connection String Specified");
                }

                return connString;
            }

            set
            {
                connString = value;
            }
        }

        private Exception errorMessage;

        public Exception ErrorMessage
        {
            get
            {
                return errorMessage;
            }

            set
            {
                errorMessage = value;
            }
        }

        #region ExecuteNonQuery
        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement)
        {
            int returnValue = 0;

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();
                    command.ExecuteNonQuery();

                    //Success
                    returnValue = 1;
                }
                catch (Exception ex)
                {
                    returnValue = 0;
                    throw ex; //ErrorMessage = ex; 
                    // WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return returnValue;
        }

        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement, List<MySqlParameter> parameters)
        {
            if ((parameters != null) && (parameters.Count > 0))
            {
                return ExecuteNonQuery(theSQLStatement, parameters.ToArray());
            }
            else
            {
                return ExecuteNonQuery(theSQLStatement);
            }
        }

        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement, MySqlParameter[] parameters)
        {
            if ((parameters == null) || (parameters.Count() <= 0))
            {
                return ExecuteNonQuery(theSQLStatement);
            }

            int returnValue = 0;

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    command.Parameters.AddRange(parameters);
                    connection.Open();
                    command.ExecuteNonQuery();

                    //Success
                    returnValue = 1;
                }
                catch (Exception ex)
                {
                    returnValue = 0;
                    throw ex; //ErrorMessage = ex; 
                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return returnValue;
        }

        #endregion

        #region Execute
        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement)
        {
            DataTable resultingDataTable = new DataTable();

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();

                    MySqlDataAdapter dataAdapter = new MySqlDataAdapter(command.CommandText, connection);
                    dataAdapter.Fill(resultingDataTable);

                    //Success
                }
                catch (Exception ex)
                {
                    throw ex; //ErrorMessage = ex; 

                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return resultingDataTable;
        }

        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement, List<MySqlParameter> parameters)
        {

            if ((parameters != null) && (parameters.Count > 0))
            {
                return Execute(theSQLStatement, parameters.ToArray());
            }
            else
            {
                return Execute(theSQLStatement);
            }
        }

        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement, MySqlParameter[] parameters)
        {
            if ((parameters == null) || (parameters.Count() <= 0))
            {
                return Execute(theSQLStatement);
            }

            DataTable resultingDataTable = new DataTable();

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();

                    MySqlDataAdapter dataAdapter = new MySqlDataAdapter(command.CommandText, connection);
                    dataAdapter.SelectCommand.Parameters.AddRange(parameters);
                    dataAdapter.Fill(resultingDataTable);

                    //Success
                }
                catch (Exception ex)
                {
                    throw ex; //ErrorMessage = ex; 
                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return resultingDataTable;
        }
    }
        #endregion
}

3 Comments

StringBuilder in this case won't make a measurable difference.
Joel is correct, it's been tested many times. And to prevent invalid casts (just in case) I'd recommend !string.IsNullOrEmpty(Session[""] as string).
good point. the "as string" is a better way than (string). ^_^
0

Figured it out, for anyone who may be experiencing anything similar.

Basically, as others above said, one part is - don't refer as much to session states. Instead I did what I did before and, as I needed my sessions in labels on the page anyway, simply read from the labels on the page as opposed to going to and from sessions all the time.

However, the main part was changing the SQL - but just a tad. Instead of using, say:

MySQLString = MySQLString + " AND [Spare1]=@S1";

I used:

MySQLString = MySQLString + " AND ([Spare1] LIKE @S1)";

Seems encapsulating each criteria and using the LIKE was the key - it now runs extremely fast in all circumstances.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.