DataReader Exception - c#

I have a simple database that I am using. It contains two entries for users which is a user with UserID 1 and IsAdmin 0 and another with UserID 3041234567 and IsAdmin of 1. The only fields in the database is a string UserID and a bit IsAdmin. I am reading from the database with the following code:
SqlConnection conn = new SqlConnection(Properties.Settings.Default.Conn);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE UserID = " + t.Text.ToString(), conn);
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
user.UserID = reader["UserID"].ToString();
user.IsAdmin = Convert.ToBoolean(reader["IsAdmin"]);
}
}
conn.Close();
If I enter the number 3041234567 as the UserID everything works perfectly, but If I enter the number 1 I get an exception saying that "The conversion of the nvarchar value '3041234567' overflowed an int column."
If I set a breakpoint and watch the while(reader.read()) loop the loop iterates through fine and sets the user.UserID = 1 and the user.IsAdmin = false. The exception is thrown when the loop begins to iterate a second time. I guess I have a couple of questions:
Why is the loop iterating a second time?
How is the ID 3041234567 being returned with the sql command "SELECT * FROM Users WHERE UserID = 1"
What is the int column that is being overflowed?

Well, since
3041234567 > int.MaxValue ( == 2147483647)
you've got an overflow; if you want some kind of integer value, however, try long (which is 64 bit long):
long value = Convert.ToInt64(reader["UserID"]);
Something like this:
// Wrap IDisposable into using
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.Conn)) {
conn.Open();
// Make sql
// 1. Readable
// 2. Parametrized
// 3. Avoid * in select
String sql =
#"select UserID,
IsAdmin
from Users
where UserID = #prm_UserId";
// Wrap IDisposable into using
using (SqlCommand cmd = new SqlCommand(sql, conn)) {
// Explicit data type will be better here (Add Parameter with type)
// but I don't know it
cmd.Parameters.AddWidthValue("prm_UserId", t.Text);
// Wrap IDisposable into using
using (SqlDataReader reader = cmd.ExecuteReader()) {
// You don't want to iterate the whole cursor, but the first record
if (reader.Read()) {
//TODO: Make UserID being "long"
user.UserID = Convert.ToInt64(reader["UserID"]);
user.IsAdmin = Convert.ToBoolean(reader["IsAdmin"]);
}
}
}
}

Related

Trying to use SQL data to multiply with other SQL data

So I am trying to use two different inputs from a user to get two different values then multiply them together to get an answer.
//code to get value
SqlCommand cmd = new SqlCommand("select Charges, Students from Subs where Subject_name='" + Subject + "'and Level='" + Level + "'", con);
//code to read and times the values
var reader = cmd.ExecuteReader();
int Price = Convert.ToInt32(reader["Charges"]);
int NumS = Convert.ToInt32(reader["Subject_name"]);
int final = (Price*NumS) / 100;
status = final + "$";
You should try something like this:
// Define **parametrized** query
string query = "SELECT Charges, Students FROM dbo.Subs WHERE Subject_name = #SubjectName AND Level = #Level;";
using (SqlCommand cmd = new SqlCommand(query, con))
{
// define the parameters and set value
// I'm just *guessing* what datatype and what length these parameters are - please adapt as needed !
cmd.Parameters.Add("#SubjectName", SqlDbType.VarChar, 50).Value = Subject;
cmd.Parameters.Add("#Level", SqlDbType.Int).Value = Level;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
int Price = reader.GetInt32(0); // index = 0 is "Charges" of type INT
// you are **NOT** selecting "Subject_Name" in your query - you therefore **CANNOT** read it from the SqlDataReader
// int NumS = Convert.ToInt32(reader["Subject_name"]);
int NumS = 1.0;
int final = (Price * NumS) / 100;
status = final + "$";
}
}
Points to ponder:
You should also put your SqlConnection con into a proper using (..) { ... } block to ensure disposal
You need to check the parameters - since you hadn't specified anything in your question, and also not posted any information about them, I can only guess
Be aware - the SqlDataReader might (and in a great many cases will) return multiple rows - which you need to iterate over
If you want to read out a column from the database table - it must be in the SELECT list of columns! You're trying to read out a column you're not selecting - that won't work, of course. ...

Read all record in sql table using SqlDataReader

I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}

SQL Ntext Items Only Having the First Two Chars Read in

I am currently programming a C# program that lets students log into an interface, check grades, etc.. Admins can create new users. The student IDs are 9-digit codes that all begin with "95." When an admin is creating a new user, I want to go through the database to make sure that the ID number they have entered isn't already taken.
To do this, I have the following code:
connection.Open();
readerUsers = commandUsers.ExecuteReader();
while (readerUsers.Read())
{
MessageBox.Show(readerUsers[2].ToString());
if(readerUsers[2].ToString() == IDNum)
{
userAlreadyExists = true;
break;
}
}
connection.Close();
And in my Users table, which readerUsers and commandUsers are connected to, I have the following:
IDuser Username 95Number Password Active Admin
-------------------------------------------------------------
1 sward 951619984 uo99lb True True
... ... ... ... ... ...
Now, when I went to test my code by creating a user with the ID number of 951619984 (a number already entered in the database), userAlreadyExists would still remain false. So I made the program show a message box of each item in the 95Number column (which is of type Ntext). Every time, the message box would only show "95".
I am very new to programming with databases, so I apologize if this is a very newby question, but I'm not sure what to do to get the whole string from this ntext column. Could someone explain what I'm doing wrong? Thank you for your time.
Here is a better way of doing that:
var connstr = ConfigurationManager.ConnectionStrings["your key"].ConnectionString;
var sql = "SELECT COUNT(*) FROM Users WHERE [95number]=#num";
using (var conn = new SqlConnection(connstr))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("num",SqlDbType.Int).Value = IDNum;
conn.Open();
var result = cmd.ExecuteScalar();
userAlreadyExists = result > 0;
}
I did mines this way.
string Qstring = "Select 95number where 95number = '95#########'";
using (SqlConnection Con = new SqlConnection(Form1.ConnectionStringGen))
using (SqlCommand Com = con.CreateCommand())
{
Com.CommandText = Qstring;
con.Open();
using (SqlDataReader Reader = Com.ExecuteReader())
{
if(Reader.Read())
{
string 95Numb = Reader["95Number"].ToString();
Messagebox.show(95Numb);
userAlreadyExists = true;
//meaning if the reader reads an item it will prompt
}
else
{
userAlreadyExists = false;
}
}
con.Close();
}
}
catch (Exception)
{
throw;
}

'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;
}

Undesired result from SQL query in C# console application

In my C# application I am trying to read data within my Accounts table, read the data as a decimal, preform a a calculation on it, and then update the same row.
Right now it reads the correct data in the column but two things go wrong when trying to update.
It sets all of the data in the AccountTotal column to the same value. This value is correct for the first row, but incorrect for the rest.
I believe the second problems occurs in calculating the data that is to be updated. When I try to update the DB, it sets the value twice as high as I am wanting it to be. For example: In my CalculateIncome method I wan't to add 100 to the account total, It adds 200.
What is causing these two problems?
Here is the program:
class Program
{
static void Main(string[] args)
{
//Need to change when deploying on real database.
const string DB_NAME = "Bank.sdf";
const string DB_PATH = #"C:\Users\Lucas\eBankRepository\eBank\App_Data\" + DB_NAME; // Use ".\" for CWD or a specific path
const string CONNECTION_STRING = "Data Source=" + DB_PATH;
decimal AccountTotal;
var conn = new SqlCeConnection(CONNECTION_STRING);
SqlCeDataReader reader = null;
try
{
conn.Open();
//Basic Query of all accounts
SqlCeCommand Query = new SqlCeCommand("SELECT * FROM Accounts", conn);
reader = Query.ExecuteReader();
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
AccountTotal += CalculateIncome();
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal", conn); // Error when using WHERE Clause "WHERE AccountName= # Savings"
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
}
}
finally
{
if (reader != null)
{
reader.Close();
}
if (conn != null)
{
conn.Close();
}
}
}
public static decimal CalculateIncome()
{
return 100;
}
}
EDIT:
Here is the code I had before that included the WHERE clause in the command. With this code, it now only updates the the rows where it has an account name of "Savings," but it still sets the value in each of the rows to be the same for AccountTotal
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
AccountTotal += CalculateIncome();
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal WHERE AccountName= #Savings", conn); // Error when using WHERE Clause "WHERE AccountName= # avings"
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Parameters.AddWithValue("#Savings", "Savings");
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
}
Here is a visual as well for before and after the program is being run.
Before
After
Working Code
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
//Console.WriteLine(AccountTotal);
AccountTotal += CalculateIncome();
//Console.WriteLine(AccountTotal);
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal WHERE AccountName = #Savings AND AccountID = #ID", conn);
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Parameters.AddWithValue("#Savings", "Savings");
UpdateTotal.Parameters.AddWithValue("#ID", reader.GetInt32(0));
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
AccountTotal = 0; //Reset
}
Your two issues are:
It's updating all the rows to be the same value
This is because there isn't a where clause in your update statement.
It's making the value double up.
This is because of the line AccountTotal += CalculateIncome();
What this does is in the first run make it be 100 and the second loop around it makes it be 200.

Categories