Click here to Skip to main content
15,899,825 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a SqlDataReader in a method "sqlSelectFreeStyle" called from a different class which is used to populate a list.

When debugging the code I can see the SqlDataReader has values, but no values are passed on to the class it is called from.

What I have tried:

Please advise what I'm missing:

Method:
C#
public  SqlDataReader sqlSelectFreeStyle(string script)
        {
            SqlDataReader rdr;
            try
            {
                SqlCommand command = new SqlCommand(script, connection);
                rdr = command.ExecuteReader(CommandBehavior.SequentialAccess);

                return rdr;
            }
            catch (Exception ex)
            {
                status = ex.Message;
                return null;
            }

        }

________________________________________________________

Below calls the SqlDataReader:
_________________________________________________________

C#
public List<string> listOfCompanies()
        {
            
            List<string> companyList = new List<string>();
            string newRecord;
            getAppDBConnectionReady();

            SqlDataReader redr = connectSQL.sqlSelectFreeStyle(
                       "SELECT * FROM [CASH_F].[dbo].[companies] " +
                       "order by company_name;");
            //add compnay names to list
            if (redr.HasRows)
            {
                while (redr.Read())
                {
                    newRecord = redr["company_name"].ToString();
                    companyList.Add(newRecord);
                }
                redr.Close();
            }
            connectSQL.closeConnection();

            return companyList;
        }
Posted
Updated 15-Apr-20 12:01pm
v2

First off, don't do it like that, it has multiple problems.

1) It is a lazy way to do it that potentially leaves you wide open to SQL Injection attack which can destroy your entire database. Always use Parameterized queries instead.

When you concatenate strings, you cause problems because SQL receives commands like:
SQL
SELECT * FROM MyTable WHERE StreetAddress = 'Baker's Wood'
The quote the user added terminates the string as far as SQL is concerned and you get problems. But it could be worse. If I come along and type this instead: "x';DROP TABLE MyTable;--" Then SQL receives a very different command:
SQL
SELECT * FROM MyTable WHERE StreetAddress = 'x';DROP TABLE MyTable;--'
Which SQL sees as three separate commands:
SQL
SELECT * FROM MyTable WHERE StreetAddress = 'x';
A perfectly valid SELECT
SQL
DROP TABLE MyTable;
A perfectly valid "delete the table" command
SQL
--'
And everything else is a comment.
So it does: selects any matching rows, deletes the table from the DB, and ignores anything else.

So providing a method to read from the DB which accepts a SELECT string is just plain dangerous!

2) Because you create the SQLCommand object in a separate method, there is no guarantee that the Command is closed, nor the Connection - in fact your code doesn't close the Reader if it has no rows.
Creating the Command and Reader inline allows you to use using blocks to ensure that they are closed and disposed when they go out of scope - and that reduces the chances of problems later.

3) Which lead us to the next problem: because you share a Connection, if any part of your code "forgets" to close the Reader that returns, then the connection is permanently busy with it - and the next query attempt will fail because a reader is open on the connection and SQL won;t let you do that. Then you have to search your code looking any way that might leave a Reader open ...

4) Don't specify SEQUENTIAL_ACCESS unless you actually need it - in which case never, ever use SELECT * because that returns all columns whether you need them or not. Since sequential access is only for really, really big columns of data, why return columns you aren't going to use and waste all that bandwidth? Chances are that's causing the HasRows test to fail as well since you have to treat the data differently when you specify it.
 
Share this answer
 
Comments
MadMyche 15-Apr-20 18:02pm    
+5
Member 14625254 16-Apr-20 7:28am    
Thanks for the prompt response and input.
First things first- please take note of Answer #1 which point out a few problems with code vulnerabilities as well as poor design which can cause problems with your connections.

In my applications, I utilize similar routines to grab the data; utilizing an encapsulate SQL class for retrieval.
What is different is that I do not return an SqlReader object; rather, I return a simple datatable.
C#
public static DataTable GetDataTableFromQuery(string connectionName, string SqlStatement, Collection<KeyValuePair<string, object>> CommandParamaterSet = null) {
	DataTable dt = null;
	using (SqlConnection conn = BuildConnection(connectionName)) {
		SqlCommand cmd = null;
		try {
			cmd = new SqlCommand(SqlStatement, conn) { CommandType = CommandType.Text };
			if (CommandParamaterSet.HasContent()) {
			   foreach (KeyValuePair<string, object> kvp in CommandParamaterSet){
					cmd.Parameters.AddWithValue(kvp.Key, kvp.Value); 		
				}
			}
			conn.Open();
			SqlDataReader dr = cmd.ExecuteReader();
			if (dr.HasRows) {
				dt = new DataTable();
				dt.Load(dr);
			}
		}
		catch (Exception ex) { throw ex; }
		finally { cmd.Dispose(); conn.Close(); conn.Dispose(); }
	}
	return dt;
}
This is an older version and does have some encapsulated/specialty items in it, but it should give you an idea on how to implement, and what you can do to if/when you need to add parameters to your query to avoid SQL Injection
 
Share this answer
 
v2
Comments
Member 14625254 16-Apr-20 7:46am    
Thanks for the prompt response and input. I like the approach of returning a DataTable.
MadMyche 16-Apr-20 7:56am    
You're welcome.
Please consider using the star-review widget to give a rating to this answer

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900