DataReader IndexOutofRangeException was unhandled by user code - c#

I ran into another issue again. I was trying to get data from the database using DataReader but I got the error when i was testing my code. Can anyone help me out? The error occurred at this line:
chkAssess = readAssess[columnName].ToString();
Below is the code snippet:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT '"+columnName+"' FROM tblAllUsers";
//MgrAssessQry += " WHERE email ='" + emailAddress + "'";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess[columnName].ToString();
}
return chkAssess;
}

try to use column name without ''
select something from table
instead of
select 'something' from table
for security reasons, don't create sql queries in that way (by concatenating strings) - use #parameters instead
2. close the reader at the end

Try this:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT #Column_Name FROM tblAllUsers";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cmdReadAssess.Parameters.AddWithValue(new SqlParameter("Column_Name", columnName));
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess.GetString(0);
}
return chkAssess;
}

You have got several problems here.
Check whether your readAssess has rows like below.
if(readAssess.HasRows)
If it doesn't have rows then trying
chkAssess = readAssess.GetString(0);
would throw this error, as Arrays are index-based.
So your code should be like below
if(readAssess.HasRows)
{
while(readAssess.Read())
{
chkAssess = readAssess.GetString(0);
}
}
Other problem is you need to close both the reader & the connection afterwards.
readAssess.Close();
cn.Close();
Also your code is potentially vulnerable to SQL Injection.

if (reader.HasRows)
{
while (reader.Read())
{
int result = Convert.ToInt32(reader.GetString(0));
Console.WriteLine(result);
}
}
The most important thing is check the query first by executing in SQL Server and see if any result is coming or not.
Secondly based on the type of output you are receiving cast it to that particular data type (important).Mostly everyone is saving the data in varchar so.

Related

C# asp.net MySQL Values return as Null

Communicating with my asp.net keeps returning a null value... I'm actually not sure why.
I've used Post Man to push information back to it to see the result excluding the use of the WPF to ensure it's my API. The only result I can return is the result I am searching for... I.E. BanID returns BanID All others return a null value.
I've Used this method for other stuff on the same API and it works fine... Somewhere I made a typo.
This is for a search box on a C# asp.net communicating with a WPF to MySQL
[HttpPost]
[Route("A3Bans/searchBan")]
public string oSearchBan(tBan ban)
{
{
tBan bans = new tBan();
string dbConnection = "datasource=127.0.0.1;port=3306;username=admin;password=123";
MySqlConnection conDataBase = new MySqlConnection(dbConnection);
MySqlCommand dbSearch;
conDataBase.Open();
MySqlDataReader dbReader;
string selectQuery = "Select * FROM a3bans.bans WHERE BanID=" + int.Parse(ban.BanID);
dbSearch = new MySqlCommand(selectQuery, conDataBase);
dbReader = dbSearch.ExecuteReader();
if (dbReader.Read())
{
tBan searchBan = new tBan();
searchBan.GuidOrIP = dbReader.GetString("GUID");
//searchBan.BanType = dbReader.GetString("BanType");
searchBan.BanReason = dbReader.GetString("Reason");
searchBan.Proof = dbReader.GetString("Proof");
bans.Equals(searchBan);
}
dbReader.Close();
return bans.GuidOrIP;
}
If BanID is PK then only one row will be returned
If there are multiple rows for one BanID, then you need to iterate dbReader
while(dbReader.Read())
{
}
Finally check your return statement.
Change
bans.Equals(searchBan);
To
bans = searchBan;

How do I take sql results and populate labels in my header?

I am attempting to change my code. I am in the process of learning while I code and many parts of my C#/ASP.Net application use LINQ to SQL and many parts use commands to directly access the sql database. I felt like I should standardize these, so I am changing over the LINQ to SQL code. I have a portion of the code that takes the finds one record based on a session variable and then populates the labels in my header with the sql query results. here is the code:
protected void Page_Load(object sender, EventArgs e)
{
var pinNum = MySession.Current.focusParcel;
if (pinNum != 0)
{
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
SqlConnection connection = new SqlConnection(sql);
connection.Open();
SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM[ParcelView] WHERE PARCEL = " + pinNum, connection);
SqlDataReader selectedParcel = command.ExecuteReader();
if (selectedParcel != null)
{
lblPIN_Num.Text = selectedParcel["PARCEL"].ToString();
lblPIN_TXT.Text = selectedParcel["PIN_TXT"].ToString();
lblDateTime.Text = DateTime.Now.ToString("MMMM dd, yyyy");
lblOwner.Text = selectedParcel["Owner"].ToString();
lblAddress1.Text = selectedParcel["Address1"].ToString();
lblAddress2.Text = selectedParcel["Address2"].ToString();
lblCSZ.Text = selectedParcel["CSZ"].ToString();
lblAcres.Text = string.Format("{0} Acres", selectedParcel["ACRES"]);
lblLegal.Text = selectedParcel["LEGAL"].ToString();
if (selectedParcel["Active"].ToString() == "A")
{
lblInactive.Text = " (ACTIVE)";
}
else
{
lblInactive.Text = " (INACTIVE)";
lnkAddDocument.Visible = false;
}
}
lblCurrentUser.Text = Page.User.Identity.Name;
connection.Close();
}
else
Response.Redirect("./ParcelSearch.aspx");
}
I am receiving the following error:
Invalid attempt to read when no data is present.
I know the SQL statement used will return a record when it is used directly into sql query of database.
Thanks in advance for any advice. Should I be concerned about the lack of consistency in accessing data across the application at all? If so, should I be converting everything to LINQ? Or converting everything away from LINQ? I klnow of the existence of MVC and know I should learn it and use it, but I am too far down the path to try to convert there right now.
Change
if (selectedParcel != null)
To
if (selectedParcel.Read())
You have to read a record in the data reader before you can start pulling data. Its a forward only cursor that starts off not pointing to a record. The Read() method advances the reader by 1 and returns true/false if it was successful.
Note that it is not necessary to call HasRows if use the code above. The if block will only be entered if there is a row, if there is not then Read returns false and the block is not entered.
Edit
There are many bad practices in your code.
Use parameterized sql!! This is the most important thing you can take away here!
Wrap your connections in using blocks to ensure the connection is ALWAYS closed.
Sql code changes
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
using(SqlConnection connection = new SqlConnection(sql))
using(SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM [ParcelView] WHERE PARCEL = #pinNum", connection))
{
command.Parameters.Add(new SqlParameter("#pinNum", SqlDbType.VarChar){Value = pinNum});
connection.Open();
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
if (selectedParcel.Read())
{
/*code unchanged*/
}
}
/* 1 line code unchanged*/
}
Once you have executed the command you need to actually read the data :
SqlDataReader selectedParcel = command.ExecuteReader();
if (selectedParcel.HasRows)
{
selectedParcel.Read();
....
....
I recommend you use this format for your sqlDatareader instead, as it it will parse whether results have been retrieved or not, and will automatically dispose itself on exception:
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
while (selectedParcel .Read())
{
//Assign label text to results
}
}
You should Read() the SqlDataReader.
protected void Page_Load(object sender, EventArgs e)
{
var pinNum = MySession.Current.focusParcel;
if (pinNum == 0)
Response.Redirect("./ParcelSearch.aspx");
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
using(SqlConnection connection = new SqlConnection(sql))
{
connection.Open();
SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM[ParcelView] WHERE PARCEL =#PinNum ", connection);
//protect from sql injection
command.Parameters.AddWithValue(#PinNum, pinNum);
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
if(!selectedParcel.HasRows)
return;
SetLabels(selectedParcel);
}
}
}
private void SetLabels(SqlDataReader selectedParcel)
{
lblPIN_Num.Text = selectedParcel["PARCEL"].ToString();
lblPIN_TXT.Text = selectedParcel["PIN_TXT"].ToString();
lblDateTime.Text = DateTime.Now.ToString("MMMM dd, yyyy");
lblOwner.Text = selectedParcel["Owner"].ToString();
lblAddress1.Text = selectedParcel["Address1"].ToString();
lblAddress2.Text = selectedParcel["Address2"].ToString();
lblCSZ.Text = selectedParcel["CSZ"].ToString();
lblAcres.Text = string.Format("{0} Acres", selectedParcel["ACRES"]);
lblLegal.Text = selectedParcel["LEGAL"].ToString();
if (selectedParcel["Active"].ToString() == "A")
{
lblInactive.Text = " (ACTIVE)";
}
else
{
lblInactive.Text = " (INACTIVE)";
lnkAddDocument.Visible = false;
}
lblCurrentUser.Text = Page.User.Identity.Name;
}
I refactor your code to look a little bit. You had different problems as not closing your Reader. You was open to sql injection, also you should transfer the connection to Db in separate class.

'Invalid attempt to read when no data is present' error

I've got this code block:
using (SqlConnection con2 = new SqlConnection(str2))
{
using (SqlCommand cmd2 = new SqlCommand(#"SELECT * FROM VW_MOS_DPL_AccountValidation WHERE CUST_NUM = #CNum", con2))
{
con2.Open();
cmd2.Parameters.AddWithValue("#CNum", TBAccountNum.Text);
using (SqlDataReader DT2 = cmd2.ExecuteReader())
{
// If the SQL returns any records, process the info
if (DT2.HasRows)
{
// If there's a BusinessID (aka Business Type), fill it in
string BizID = (DT2["Business_ID"].ToString());
if (!string.IsNullOrEmpty(BizID))
{
DDLBustype.SelectedValue = BizID;
}
}
}
con2.Close();
}
}
When it gets to the line
string BizID = (DT2["Business_ID"].ToString());
it throws an error:
Invalid attempt to read when no data is present.
Why would it get past if (DT2.HasRows) if there was no data?
You need to call
if(DT2.Read())
....
before proceding to read data from a DataReader.
The HasRows tells you only that the SqlDataReader contains data, but the SqlDataReader loads one record at time from the connection. Thus every tentative to extract the data from the SqlDataReader should be preceded by a call to Read to position the SqlDataReader on the first record returned through the connection.
And, because the Read method returns true if the call has been able to read a record, you could replace the call to HasRows with something like this
using (SqlDataReader DT2 = cmd2.ExecuteReader())
{
// If the SQL returns any records, process the info
while(DT2.Read())
{
// If there's a BusinessID (aka Business Type), fill it in
string BizID = (DT2["Business_ID"].ToString());
if (!string.IsNullOrEmpty(BizID))
{
DDLBustype.SelectedValue = BizID;
}
}
}
By the way, if it is possible to have a NULL for BusinessID then you need a different test to avoid exception problems
int bizColIndex = DT2.GetOrdinal("Business_ID");
string BizID = (DT2.IsDBNull(bizColIndex) ? string.Empty : DT2.GetString(bizColIndex));
if (!string.IsNullOrEmpty(BizID))
{
DDLBustype.SelectedValue = BizID;
}

Why is my returned DataSet instance has no tables?

When I connect to the DB and I want to read data from table it doesn't work an shows these
Exception Details:
System.IndexOutOfRangeException: Cannot find table 0.
Error in Line 141
I have only one table in the dataset. Do you know the best way to read from table for me ? I return only one table. When I use foreach, how can I read a one row data?
internal DataSet read(string query)
{
DataSet nds = new DataSet();
try
{
string connectionString = "...";
try
{
SqlConnection nsqlc = new SqlConnection(connectionString);
SqlCommand get = new SqlCommand(query, nsqlc);
nsqlc.Open();
SqlDataAdapter nsda = new SqlDataAdapter(get);
nsda.Fill(nds);
nsqlc.Close();
}
catch (Exception)
{
}
return nds;
}
catch (Exception)
{
return nds;
}
}
Main form :
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
String links = "";
links = "movie%";
DataSet t = n.read("select id , poster from Movie order by id desc");
for (int i = 0; i < 10; i++)
{
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
links = links + t.Tables[0].Rows[i][1] + "%";
}
The closest thing to an answer here would have to be that the n.read method returns a DataSet instance that has no tables in it. You need to step into the method with a debugger and figure out why it behaves like that.
If you see what the problem is, fix it. If not, share the read method code and someone will be able to help you further.
Looking at your updated post, I see that you are swallowing every possible exceptions that could occur while trying to fetch data. Remove the catch block from your read method. This will allow you to see what the real problem is.
Exception swallowing is something you'll want to take away from your code on a global scale. I'd suggest that you do some Googling to find out why it's a terrible habit.
About a "better way to read from table", you should consider using a IDataReader.
string links = "movie%";
using (var connection = new SqlConnection("your connection string");
{
using (var command = someExistingConnection.CreateCommand())
{
command.CommandText = "select id, poster from Movie order by id desc";
command.Connection = connection;
connection.Open();
try
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var idValue = reader.GetObject(0).ToString(); // would be better to use actual field type, which is unknown at the time I'm writing this
var posterValue = reader.GetString(1);
// perform concatenation here using the two variables declared above
links += String.Format("{0}%{1}%", idValue, posterValue);
}
}
}
finally
{
connection.Close();
}
}
}
This will outperform working with a DataSet by a long shot.

How to make Sqlcommand accept null values

I'm trying to get data in a gridview from a database to show up in text boxes upon clicking and it works fine for the rows with no null data, although since my int columns have some null values my GetInt32 methods keep returning "Data is Null. This method or property cannot be called on Null values."
Is there a simple way to fix or work around this? Do I replace GetInt32 with another method? I'd like for the data that is null to show up blank/empty in the text boxes if possible. Here's my code if you have any suggestions, thanks.
public ArrayList GetAllPersonnel(int WorkerID) {
using (var connection = new SqlConnection(connectionString)) {
connection.Open();
String query = "Select * FROM Personnel WHERE WorkerID = " + WorkerID;
using (var command = new SqlCommand(query, connection)) {
var reader = command.ExecuteReader();
var list = new ArrayList();
while (reader.Read()) {
String firstname = reader.GetString(1);
String lastname = reader.GetString(2);
String occupation = reader.GetString(3);
String deployment = reader.GetString(4);
int disasterid = reader.GetInt32(5);
String location = reader.GetString(6);
int deployedhours = reader.GetInt32(7);
int resthours = reader.GetInt32(8);
list.Add(firstname);
list.Add(lastname);
list.Add(occupation);
list.Add(deployment);
list.Add(disasterid);
list.Add(location);
list.Add(deployedhours);
list.Add(resthours);
}
connection.Close();
reader.Close();
return list;
}
}
}
You should use IsDBNull method of the SqlDataReader
int resthours = (!reader.IsDBNull(8) ? reader.GetInt32(8) : 0);
or, more directly
list.Add((!reader.IsDBNull(8) ? reader.GetInt32(8).ToString(): string.Empty));
Said that, I have noticed that you use a string concatenation to build the sql command text to retrieve records. Please do not do that. It is very dangerous and could lead to Sql Injection
String query = "Select * FROM Personnel WHERE WorkerID = #wkID";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("#wkID", WorkerID);
var reader = command.ExecuteReader();
....
OK, so you're effectively saying that everything you display should be a string type, which is fine, I'm just making that point because you stated you want even integers to show up as an empty string. So how about this code?
String firstname = reader.GetString(1);
String lastname = reader.GetString(2);
String occupation = reader.GetString(3);
String deployment = reader.GetString(4);
String disasterid = reader.IsDBNull(5) ? string.Empty : reader.GetString(5);
String location = reader.GetString(6);
String deployedhours = reader.IsDBNull(7) ? string.Empty : reader.GetString(7);
String resthours = reader.IsDBNull(8) ? string.Empty : reader.GetString(8);
list.Add(firstname);
list.Add(lastname);
list.Add(occupation);
list.Add(deployment);
list.Add(disasterid);
list.Add(location);
list.Add(deployedhours);
list.Add(resthours);
Now, the reason I stated that you want to leverage everything as a string is because the default value for a int is 0 and that wouldn't meet the empty text box requirement.
You have at least two ways to sort this out
Modify your sql to select either zero or whatever you think suitable in the place of null value. This will ensure that you always have an integer value in the integer column. It can be done in the following manner
select ISNULL ( ColumnName , 0 ) as ColumnName from xxx
Always fetch object from the reader and check if it is null or not. If it is null then replace it with suitable value.

Categories