SqlDataReader returns null unexpectedly - c#

I have a simple connection to a SQL Server that is not working. I'm trying to read data from it using a SqlDataReader in C#.
Here is the code:
bool ok = false;
SqlConnection con = new SqlConnection();
con.ConnectionString = #"********";
SqlCommand cmd = new SqlCommand();
cmd.Parameters.AddWithValue("#a", uname);
cmd.Parameters.AddWithValue("#b", pass);
cmd.CommandText = #"SELECT username FROM admins WHERE pass='#b'";
cmd.Connection = con;
SqlDataReader r;
con.Open();
r = cmd.ExecuteReader();
r.Read();
string n;
n = r.GetString(0);
if (n != null)
{
ok = true;
}
con.Close();
if (ok)
{
Session["admin"] = uname;
Response.Redirect("admin_page.aspx");
}
else
{
eror.Text = "An eror occured";
Response.Redirect("index.aspx#work");
}
Note: that in the above code string "uname" and "pass" are definitely not null.
Note #2 : I did try running the r.read() in a while loop (even though it's not possible to have more then one row) ---> same result.
I tried running this code in step mode, and it appears that it breaks on this line:
n = r.GetString(0);
With this exception:
An exception of type 'System.InvalidOperationException' occurred in System.Data.dll but was not handled in user code
Additional information: Invalid attempt to read when no data is present.
I'm kinda lost here. I know that it's probably a simple thing I missed here, I just can't find it. Any ideas?

In addition to Amit's observation about an unused parameter, you are misusing the parameter
Where you have
cmd.CommandText = #"SELECT username FROM admins WHERE pass='#b'";
you should not have quotes around the value, so:
cmd.CommandText = #"SELECT username FROM admins WHERE pass=#b";
The parameter will know it is a VARCHAR

Your not using SqlDataReader correctly.
If you want to know whether there exists a user with the correct username and password, your query should probably be: SELECT 1 FROM admins WHERE username=#a AND pass=#b
Once that's done, and since you don't care what the selected value is (you only care that there was a returned row...) Use the command like this:
r = cmd.ExecuteReader();
ok = r.HasRows;
After this, continue as you did.

Related

Error on passing values in SqlCommand query

What am I doing wrong here? I just want a dynamic method which can count any column depending on the value. But getting a runtime error.
An exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll but was not handled in user code
Additional information: Incorrect syntax near '='."
public class DataAccessLayerPayroll
{
public static string CountTblColumByValue(string columName,string value)
{
String cs = ConfigurationManager.ConnectionStrings["BD_CompanyConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(cs))
{
string sqlQuery ="Select Count("+columName+") from tblAttendance1 where"+columName+"='"+value+"'";
SqlCommand cmd = new SqlCommand(sqlQuery, con);
con.Open();
object count = cmd.ExecuteScalar();
return count.ToString();
}
}
}
The issue here is that you're missing some space between where "+columName+"= '"+value+"'"
But the bigger issue is that your command is exposed to SQL injection. To prevent this use parameterized queries.
Change it as the following way
public static string CountTblColumByValue(string columnName, string value)
{
String cs = ConfigurationManager.ConnectionStrings["BD_CompanyConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(cs))
{
string sqlQuery ="Select Count(#myColumName) from tblAttendance1 where #myColumName = #myValue ";
using(SqlCommand cmd = new SqlCommand(sqlQuery, con))
{
con.Open();
cmd.Parameters.AddWithValue("#myColumName", columnName);
cmd.Parameters.AddWithValue("#myValue", value);
object count = cmd.ExecuteScalar();
return count.ToString();
}
}
}
string sqlQuery ="Select Count("+columName+") from tblAttendance1 whereYOU ARE MISSING A SPACE HERE"+columName+"='"+value+"'";
So your code must be like following:
string sqlQuery ="Select Count("+columName+") from tblAttendance1 where "+columName+"='"+value+"'";
The basic error in your code is missing space between where and =
Also, You should definitely fix the SqlInjection as suggested in other answers. (You cannot use parameters for column name though)
After that, you also should see that according to Count definition you will only get count of not null values.
Count() function returns the number of records in a select query (only Not Null) values.
Also see how will you achieve following:
1) count null values only
2) count all not null values
3) count number/date operations i.e. <, >, between
If you don’t need these scenarios in your code, then you have the answer to the error in your sql.

C# to SQL ExecuteReader() getting stuck

When running the below method in my application the app freezes and when I pause VS it seems to be stuck on the line that goes:
SqlDataReader reader = select.ExecuteReader();
I've got other SQL methods running fine so I know the connection string is correct, I've double checked the SQL and that's fine. Am I wrong in think the reader variable can not contain the returning value of the scalar function when the ExecuteReader() is called?
public static bool AccountValidation(string username, string password)
{
string statement = "select dbo.AccountValidation('" + username + "','" + password + "')";
SqlCommand select = new SqlCommand(statement, connect);
connect.Open();
SqlDataReader reader = select.ExecuteReader();
string result = reader.ToString();
connect.Close();
if (result != "true")
{
return false;
}
else
{
return true;
}
}
The main problem is that you are not actually reading anything back from the data reader, you have to iterate over the result set and then read based on ordinal/positional index.
There are also other big problems like
Not using parameters which leaves the code vulnerable to sql injection attacks.
Not wrapping your disposables in using blocks which could leave database connections open if there are exceptions
sharing db connections in your types. Create connections on an as needed basis and then dispose them when you are done.
Here is your updated code with fixes. I guessed at the column types (varchar), fix that and the lengths as they are implemented in your schema.
public static bool AccountValidation(string username, string password)
{
const string statement = "select dbo.AccountValidation(#username, #password)";
string result = null;
// reference assembly System.Configuration
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["YourDb"].ConnectionString;
using(var connection = new SqlConnection(connStr))
using(SqlCommand cmd = new SqlCommand(statement, connect))
{
cmd.Parameters.Add(new SqlParameter("#username", SqlDbType.VarChar, 200){Value = username});
cmd.Parameters.Add(new SqlParameter("#password", SqlDbType.VarChar, 200){Value = password});
connect.Open();
using(SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.Read())
result = reader.GetString(0); // read back the first column of the first row
}
}
if (result != "true")
{
return false;
}
else
{
return true;
}
}
On a side note it would be cleaner to return a bit from your database function AccountValidation and then read that back with reader.GetBoolean(0) and assign that to the result and return that directly instead of doing string comparisons.
Also, as mentioned above in the comments, if you are only returning 1 value it is easier (and less code) to call ExecuteScalar instead of ExecuteReader.
Add the line
reader.Read();
before the line
string result = reader.ToString();
Also, please parameterize your queries.
I don't have enough reputation to leave a comment, but to answer part of your question, yes, you can use a SqlDataReader to read single scalar results with or without column aliases.

Getting column information in SQL

I am somwhat new to SQL, so I am not sure I am going about this the right way.
I am trying to fetch data from my SQL Server database where I want to find out if checkedin is 1/0, but it needs to search on a specific user and sort after the newest date as well.
What I am trying to do is something like this:
string connectionString = ".....";
SqlConnection cnn = new SqlConnection(connectionString);
SqlCommand checkForInOrOut = new SqlCommand("SELECT CHECKEDIN from timereg ORDER BY TIME DESC LIMIT 1 WHERE UNILOGIN = '" + publiclasses.unilogin + "'", cnn);
So my question, am I doing this right? And how do I fetch the data collected, if everything was handled correctly it should return 1 or 0. Should I use some sort of SqlDataReader? I am doing this in C#/WPF
Thanks
using (SqlDataReader myReader = checkForInOrOut.ExecuteReader())
{
while (myReader.Read())
{
string value = myReader["COLUMN NAME"].ToString();
}
}
This is how you would read data from SQL, but i recommend you looking into Parameters.AddWithValue
There are some errors in your query. First WHERE goes before ORDER BY and LIMIT is an MySql keyword while you are using the Sql Server classes. So you should use TOP value instead.
int checkedIn = 0;
string cmdText = #"SELECT TOP 1 CHECKEDIN from timereg
WHERE UNILOGIN = #unilogin
ORDER BY TIME DESC";
string connectionString = ".....";
using(SqlConnection cnn = new SqlConnection(connectionString))
using(SqlCommand checkForInOrOut = new SqlCommand(cmdText, cnn))
{
cnn.Open();
checkForInOrOut.Parameters.Add("#unilogin", SqlDbType.NVarChar).Value = publiclasses.unilogin;
// You return just one row and one column,
// so the best method to use is ExecuteScalar
object result = checkForInOrOut.ExecuteScalar();
// ExecuteScalar returns null if there is no match for your where condition
if(result != null)
{
MessageBox.Show("Login OK");
// Now convert the result variable to the exact datatype
// expected for checkedin, here I suppose you want an integer
checkedIN = Convert.ToInt32(result);
.....
}
else
MessageBox.Show("Login Failed");
}
Note how I have replaced your string concatenation with a proper use of parameters to avoid parsing problems and sql injection hacks. Finally every disposable object (connection in particular) should go inside a using block

MySqlCommand Prepare() never sets IsPrepared to true

Here's the issue: no matter what I seem to do, I can't get a MySqlCommand to actually prepare. I've tried copy/pasting the example code from http://dev.mysql.com/doc/refman/5.6/en/connector-net-programming-prepared.html with very slight modifications, but that does not actually work either.
I scoured Google to try and find a solution, but the closest thing that came up was: MySql statement prepare "not sticking" which did not actually answer the question.
Here's my table setup for this test:
CREATE TABLE `test`.`test_prepared_query` (
`id` INT NOT NULL ,
`value` INT NOT NULL ,
PRIMARY KEY (`id`) );
Test code in C#
public void TestPrepareQuery()
{
connString = new MySqlConnectionStringBuilder();
connString.Server = "localhost";
connString.Database = "test";
connString.UserID = "someuserid";
connString.Password = "somepassword";
bool isprepared;
using (MySqlConnection conn = new MySqlConnection(connString.ToString()))
{
MySqlCommand cmd = new MySqlCommand(
"INSERT INTO test_prepared_query VALUES (#id, #value)", conn);
cmd.Connection.Open();
cmd.Prepare();
isprepared = cmd.IsPrepared; // isprepared is false here
cmd.Parameters.AddWithValue("#id", 0);
cmd.Parameters.AddWithValue("#value", 0);
cmd.Prepare();
isprepared = cmd.IsPrepared; // isprepared is still false
// this is 1 -- the query succeeds
int rowsAffected = cmd.ExecuteNonQuery();
for (int i = 1; i < 10; i++)
{
cmd.Parameters["#id"].Value = i;
cmd.Parameters["#value"].Value = i;
// this is 1 -- the query succeeds
rowsAffected = cmd.ExecuteNonQuery();
}
}
}
When I run the code, it does successfully put rows in the table, but stepping through the program reviews that the state of cmd.IsPrepared is always false. Does anyone know why this could be happening? This source code is essentially identical to the example code, with only modifications to the table name as well as real connection strings.
Edit:
I've tried variables with ?name format, and that does not work either. I've also tried only having one cmd.Prepare() method call at a time in the tests.
I eventually pulled up the source code for MySql Connector / .NET and discovered that if MySqlCommand.Connection.Settings.IgnorePrepare = true (which is the default!), then calling Prepare is a no op.
The way to fix this is to explicitly set IgnorePrepare to false in the connection string. This can be done rather easily with a MySqlConnectionStringBuilder using the following code snippet:
MySqlConnectionStringBuilder connBuilder = new MySqlConnectionStringBuilder();
// .. set up the rest of your connection
connBuilder.IgnorePrepare = false;
MySqlConnection conn = new MySqlConnection(connBuilder.ToString());

select one entry sql

I'm trying to select just one entry from a database. It is currently returning an xml document object and I can't figure out why. Atleast, thats what my javascript is telling me. I want it to return a string that is the name fo the gameRequestUser where userName="this user"
try {
SqlConnection conn = new SqlConnection(#"Data asdfasdf;database=asdfsdfdf;User id=asdfasdfasdfPassword=asdfasdf;");
SqlCommand getRequest = new SqlCommand("SELECT gameRequestUser FROM UserData Where userName='" + Session["userName"].ToString() + "'", conn);
conn.Open();
SqlDataReader reader = getRequest.ExecuteReader();
while (reader.Read()) {
user = reader.GetValue(0).ToString().Trim();
}
conn.Close();
return user;
} catch (Exception e) { return e.Message.ToString(); }
You should use ExecuteScalar instead of ExecuteReader:
user = (string)getRequest.ExecuteScalar();
And even before you should check your query results using SQL Server Management Studio - run the query there and check if the results are OK.
Always use parameters, you avoid too many problems (string quote, sql injections etc)
using(SqlConnection conn = new SqlConnection("yourconnectionstring"))
{
SqlCommand getRequest = new SqlCommand("SELECT gameRequestUser FROM UserData Where " +
"userName=#user", conn);
conn.Open();
getRequest.Parameters.AddWithValue("#user",Session["userName"].ToString())
SqlDataReader reader = getRequest.ExecuteReader();
while (reader.Read()) {
user = reader.GetValue(0).ToString().Trim();
}
}
One thing you should do is go into SQL Server Management studio, and try running the query there directly:
SELECT gameRequestUser FROM UserData Where userName='this user'
That being said, another thing to keep in mind is you can tell SQL to return to you at most 1 row by doing something like:
SELECT top 1 gameRequestUser FROM UserData Where userName='this user'
I hope this helps!
Use a SELECT TOP 1 ... query
SELECT TOP 1 gameRequestUser FROM UserData WHERE ...
Use SqlCommand's ExecuteScalar() method instead of ExecuteReader(), since you only need one field value returned.
SqlCommand getRequest = new SqlCommand(....);
...
string user = Convert.ToString(cmd.ExecuteScalar());

Categories