Read a value from the database and save it into a variable - c#

I want to access my SQL Server database and retrieve the value of some columns with specific id. I get the id as a print the database in a dataGridView table, so the first cell of the row that the user has selected is the id. Here is the code
con3.Open();
if (typeBOX.SelectedIndex == 0)
{
pictureBox1.Image = Organizer.Properties.Resources.b_height_years;
ChartForm.ImageSet = 1;
pictureBox1.Invalidate();
SqlCommand cmd1 = new SqlCommand("select Height,Age from data where Id = '" + dataGridView1.SelectedCells[0].Value.ToString() + "'", con3);
// SqlCommand cmd2 = new SqlCommand("select Age from data where Id = '" + dataGridView1.SelectedCells[0].Value.ToString() + "'", con3);
SqlDataReader reader1 = cmd1.ExecuteReader();
bool flag = false;
while (reader1.Read() && flag == false)
{
string tempHeight = reader1["Height"].ToString();
ChartForm.Height = int.Parse(tempHeight);
string tempAge = reader1["Age"].ToString();
ChartForm.Age = int.Parse(tempAge);
flag = true;
}
}
But when I am trying to run the code I get the error:
System.Data.SqlClient.SqlException: 'Conversion failed when converting the varchar value 'zxxv' to data type int.
'zxxv' is a saved FirstName in the database but I do not for it in my command cmd1. I am only accessing the height and age which are both integers. I do not know why this is happening.

I bet you received that error because you tried to pull the Id out of the wrong cell in the data. The error message states that an error occurred while trying to convert value 'zxxv' to int. That would mean that the value stored in:
dataGridView1.SelectedCells[0]
is 'zxxv'. An error is occurring when the SQL Server attempts to compare that value to an int column. It looks like maybe you clicked on the cell in the dataGridView1 that contained the first name, but the id is in a different column, or is hidden. This is just a wild guess, but it's the best I can do without having the rest of your code. Would something like this fix it?
dataGridView1.SelectedRows[0].Cells[0].Value
or:
dataGridView1.SelectedRows[0].Cells["Id"].Value
I totally agree with the others who said you should pass the id in as a parameter. You should do that too. But I don't think the lack of a parameter is what is causing the error.

The problem is you're passing the Id as a string, not an int. So, the error is telling you that the SQL server failed to convert the string into an int. The reason this is happening is because the Id is wrapped in single quotes which SQL will interpret as a string (varchar/nvarchar).
I'd recommend to always parameterise your SQL query to avoid potential attacks through SQL injection and problems like this one. Please see Why do we always prefer using parameters in SQL statements?.
You also need to ensure you're selecting the correct value from the dataGridView as #RussW mentions in their answer. You're selecting the FirstName field, so perhaps you could use:
int selectedRowIndex = datagridview1.SelectedCells[0].RowIndex;
DataGridViewRow selectedRow = datagridview1.Rows[selectedRowIndex ];
int id = int.Parse(selectedRow.Cells["Id"].Value.To);
Or
int rowIndex = dataGridView1.CurrentCell.RowIndex;
int columnIndex = dataGridView1.CurrentCell.ColumnIndex;
int id = int.Parse(dataGridView1.Rows[rowIndex].Cells[columnIndex].Value.ToString());
Below is a sample which should get you started:
string query = "select Height, Age from data where Id = #id"; // parameter #id in string
SqlCommand cmd1 = new SqlCommand(query, con3); // create command with string
// get the correct row and cell
int selectedRowIndex = dataGridView1.SelectedCells[0].RowIndex;
DataGridViewRow selectedRow = dataGridView1.Rows[selectedRowIndex];
int id = int.Parse(selectedRow.Cells["Id"].Value.ToString()); // parse id to int
// create sql parameter and add to SqlCommand
SqlParameter param1 = new SqlParameter("#id", id);
cmd1.Parameters.Add(param1);
// continue your code...
SqlDataReader reader1 = cmd1.ExecuteReader();
....
Note, when using parameterised queries there's no need to wrap the parameter in quotes, it is handled for you. Please refer to the SqlCommand.Parameters docs.

Related

IndexOutOfRange Exception in sqldatareader using c#

I create an application using c# , In my authentification interface , i have a test control , i want to know profile user .
My database contains table named user which contains 4 columns
(id_user,name ,mail, profile)
Here is my code
public string profil_user(string login)
{
SqlConnection conn = new database().connect_user();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select profile from user where name = '" + login + "';";
SqlDataReader s = cmd.ExecuteReader();
if (s.Read())
{
return ( s.GetString(3));
}
else{return ("false"); }
}
but i have an exception in s.GetString(3)
system.IndexOutOfRange : index was outside the bounds of the array
You're only selecting a single field (profile) but then you're trying to select the 4th field (index 3) here:
return ( s.GetString(3));
In addition to just returning s.GetString(0) I would strongly advise you to:
Use parameterized SQL - always do this, to prevent SQL injection attacks, make your code more readable, and prevent unexpected text conversion problems
Either throw an exception or return null if the profile isn't found, instead of returning the string "false"
Use using statements for disposable things like SqlCommand, SqlConnection and SqlDataReader to ensure that you clean up resources appropriately
Start following .NET naming conventions to make your code more idiomatic
So something like:
public string GetUserProfile(string login)
{
string sql = select profile from user where name = #login";
// I assume Connect() returns an *open* connection?
using (var conn = new Database().Connect())
{
using (var command = new SqlCommand(sql, conn))
{
command.Parameters.Add("#login", SqlDbType.NVarChar).Value = login;
using (var reader = command.ExecuteReader())
{
// If it's an error (code failure) for there to be no matching profile,
// you may want to throw an exception instead.
return s.Read() ? s.GetString(0) : null;
}
}
}
}
So you want the fourth row, not the fourth column which you try to access with s.GetString(3):
int rowNum = 0;
while(s.Read())
{
if(++rowNum == 4)
{
return s.GetString(0);
}
}
return "false";
However, it is a bit strange to access the fourth row when you don't use an Order By. You should also only return the row that you want with the correct sql query.
You are also open for sql injection if you use string concatenation here:
cmd.CommandText = "select profile from user where name = '" + login + "';";
Use sql parameters:
cmd.CommandText = "select profile from user where name = #login";
cmd.Parameters.Add("#login", SqlDbType.VarChar).Value = login;
have 4 columns not rows
Ok, so you instead want the fourth column. Why don't you use the name instead?
Since you only select the profile-column(the fourth), you could simply use GetString(0). But you could also select all columns and then determine the correct index with GetOrdinal:
int profileColumnIndex = s.GetOrdinal("profile");
return s.GetString(profileColumnIndex);
This is useful if you don't control the query or it might be changed in future.
You are selecting only 1 field, thus index 3 is out of bounds. It also very important to Use parameters. Try:
cmd.CommandText = "select profile from user where name = #login;";
cmd.Parameters.Add("#login, SqlDbType.NVarChar).Value = login;
SqlDataReader s = cmd.ExecuteReader();
while (s.Read())
{
return s[0].ToString();
}
The parameter for SqlDataReader.GetString should be the column index. You're only selecting one column so you get an exception.
Because you do not have all the fields in your select list
Change the SQL to:
select id_user,name ,mail, profile from user where name = '" + login + "';

c# SqlDataReader Not Finding Value

My Problem has Been Fixed, My main problem was getting the information from the textbox in the xaml which got erased after that window was closed and another opened. Though the answers did fix my other problems and have made my code much simpler and easier to read. So thank you very much!
So I am Currently working on building a Calendar for a personal project and working on adding events to a Database, this table for Events stores two varchars, and an int (name, description, userid), the userid is a foreign key and is linked to the User Table. When I use the code below to try and pull the userid for the username that the person entered, it tells me that there is no existing value.
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString =
"Data Source=calenderserver.database.windows.net;" +
"Initial Catalog=Calender;" +
"User id=*******;" +
"Password=*******;" +
"MultipleActiveResultSets = true";
connection.Open();
SqlCommand com = new SqlCommand("Select UserId from Users Where UserName = #user", connection);
com.Parameters.AddWithValue("#user", UsernameTextBox.Text);
SqlDataReader reader = com.ExecuteReader();
reader.Read();
int userid = reader.GetInt32(1);
messages.Text = "Event Added";
SqlCommand command = new SqlCommand("INSERT INTO [Events] VALUES (#eventname, #eventdesc)", connection);
command.Parameters.AddWithValue("#eventname", name);
command.Parameters.AddWithValue("#eventdesc", description);
command.Parameters.AddWithValue("#userid", userid);
command.ExecuteNonQuery();
reader.Close();
connection.Close();
}
Even though when I run the same command in an actual SQL Query it returns a proper value.
SQL Command
I am completely lost on this and have checked multiple sources and solutions and would really appreciate the help.
You are doing int userid = reader.GetInt32(1); the indexes for the get function are 0 based so you actually need int userid = reader.GetInt32(0); so you get the first column.
That being said, because you are using the first result of the first column you can simplify your code by switching from a data reader to using ExecuteScalar()
SqlCommand com = new SqlCommand("Select UserId from Users Where UserName = #user", connection);
com.Parameters.AddWithValue("#user", UsernameTextBox.Text);
int userid = (int)com.ExecuteScalar();
Try using ExecuteScalar function. Execute scalar returns a single value and I see you only need the user ID.
Take a look at this link .
int userid = (Int32)com.ExecuteScalar();
I Hope it helps!
Indices in GetInt32 are 0-based as per doc, therefore your call should read:
int userid = reader.GetInt32(0);
Change these lines:
SqlDataReader reader = com.ExecuteReader();
reader.Read();
int userid = reader.GetInt32(1);
to:
var userID = com.ExecuteScalar();
Why:
Execute Scalar should be used when your query returns a single value.
Execute Reader returns a collection of data in the form of a DataReader. DataReaders are fast, and you can quickly iterate over them to get the data you need from the database. The connection remains open as long as the datareader is open.
Because you were only getting a single value back from the database, it makes sense to use ExecuteScalar. It's more efficient and too the point.
If you were getting a list of UserID's, then I'd recommend you use a DataReader to iterate through the UserIDs.

Take the ID of an Table after Select Query in c#

How can I take the ID as a variable from the Select query in c#.
I have written a query which has to select all IDs with a specific Devicetype.
sqlCommand = new SqlCommand("SELECT Id FROM T_Table1 WHERE DeviceType='" + dev + "'" + "", DbConnect());
int id = (int)sqlCommand.ExecuteScalar();
That's the exception:
An unhandled exception of type 'System.InvalidCastException' occurred in test.exe
Additional information: Specified cast is not valid.
First of all you have to check the database type of your 'id' column. If it is bigint, then use long instead of int.
And secondary you should not add the query string to the select statement. That enables hackers later to run a sql injection against your app or service.
Please use Parameters with the SqlCommand object.
string sql= "SELECT Id FROM T_Table1 WHERE DeviceType=#dev";
SqlCommand command = new SqlCommand(sql, DbConnect());
command.Parameters.Add("#dev", SqlDbType.Text);
command.Parameters["#dev"].Value = dev;
long id = (long)command.ExecuteScalar();
try this
int id =Convert.ToInt32(sqlCommand.ExecuteScalar());
You're calling Execute scalar which will return the first value of the first column of the returned datarow IIRC... So you are trying to take the integer value of null, which others have stated, cannot occur.
I would suggest using the ExecuteEnumerable flavor to return a set of Datarows containing your ID's from T_Table1
using (var myCommand = new SqlCommand())
{
var query = string.Format("SELECT ID FROM T_Table1 WHERE DeviceType='{0}'", dev);
var rows = myCommand.ExecuteEnumerable(query);
foreach (var row in rows)
{
// do whatever to each row
var id = row["ID"];
}
}
If the SELECT query finds no matching value in the database, then the return value of sqlCommand.ExecuteScalar() will be null, if I rembember correctly. And null value cannot be cast to int. You need to check the returned value first before doing the cast. Additionally, you might want to change the type to long instead of int, because an int cannot hold all possible bigint values.

Retrieve data from sql server compact 4.0 to textbox

I have two text boxes in my winform. I would like to enter a userId into first text box, after that by clicking a button display a User Name in the second text box properly. The data is stored in sql server compact. Table name is Users, and this table contains two columns UserID and UserName.
With this code I can open a connection and retrieve the first value from the UserName column,
SqlCeConnection cn = new SqlCeConnection(#"Data Source = D:\Database\Training.sdf");
try
{
cn.Open();
SqlCeCommand cmd = new SqlCeCommand("SELECT UserID, UserName from Users;", cn);
TrainerNameBox.Text = cmd.ExecuteScalar().ToString();
cn.Close();
}
catch
{
}
ExecuteScalar returns first column of the first row. Other columns or rows are ignored.
In your case, your first column is UserID. That's why you get first value of this column.
If you want to get UserName value, you might need to change your query like;
SELECT UserName from Users
And looks like you forget to use WHERE clause in your query since you want to get UserName from UserID. You might need to use using statement to dispose your SqlCeConnection and SqlCeCommand.
Full example;
using(SqlCeConnection cn = new SqlCeConnection(#"Data Source = D:\Database\Training.sdf"))
using(SqlCeCommand cmd = cn.CreateCommand())
{
cmd.CommandText = "SELECT UserName from Users WHERE UserID = #id";
cmd.Parameters.AddWithValue("#id", (int)txtUserID.Text);
cn.Open();
TrainerNameBox.Text = cmd.ExecuteScalar().ToString();
}
You are missing the WHERE clause to isolate the username that you want to display
int userID;
if(!Int32.TryParse(txtUserID.Text, out userID))
{
MessageBox.Show("Invalid User ID number");
return;
}
using(SqlCeConnection cn = new SqlCeConnection(#"Data Source = D:\Database\Training.sdf"))
using(SqlCeCommand cmd = new SqlCeCommand("SELECT UserName from Users WHERE UserID=#id;", cn))
{
cn.Open();
cmd.Parameters.AddWithValue("#id", userID);
object result = cmd.ExecuteScalar();
if(result != null)
TrainerNameBox.Text = result.ToString();
else
MessageBox.Show("No user for ID=" + userID.ToString());
}
Notice that ExecuteScalar returns the first column of the first row, so you need to remove the UserID field from your query and if, the user is not found, you need to check for a null return.
Applying directly the ToString() method to your ExecuteScalar could raise an exception if your user types an invalid id. There is also the problem to validate the user input. If you type a not numeric value for the user id, the conversion will fail. In this case you need to check the input using Int32.TryParse
Try this:
Dataset ds = cmd.ExecuteDataset().ToString();
TrainerNameBox.Text = ds.tables[0].Rows[0][1].toString();
TrainerIDBox.Text = ds.tables[0].Rows[0][0].toString();

Can't find stored procedure parameters in code-behind

The Overview: I've got a dropdown with a list of reports the user can run. In the table that holds this list, I have ReportID, ReportName, SProc and SQLView fields. The idea here is, the user selects a report name, and based on that a specific Stored Procedure will run, and then a specific view will be bound to a datagrid to display the report. For some reports you need to enter a date, for others you don't.
The Code: Here is what I have written;
protected void btnSubmit_OnClick(object sender, EventArgs e)
{
List<ReportData> myReportData = new List<ReportData>();
using (SqlConnection connection1 = new SqlConnection(str2))
{
//Query the Reports table to find the record associated with the selected report
using (SqlCommand cmd = new SqlCommand("SELECT * from tblManagerReports WHERE ReportID = " + cboFilterOption.SelectedValue + "", connection1))
{
connection1.Open();
using (SqlDataReader DT1 = cmd.ExecuteReader())
{
while (DT1.Read())
{
//Read the record into an "array", so you can find the SProc and View names
int MyRptID = Convert.ToInt32(DT1[0]);
string MyRptName = DT1[1].ToString();
string MyRptSproc = DT1[2].ToString();
string MySQLView = DT1[3].ToString();
//Run the Stored Procedure first
SqlConnection connection2 = new SqlConnection(str2);
SqlCommand cmd2 = new SqlCommand("" + MyRptSproc + "", connection2);
//Set up the parameters, if they exist
if (string.IsNullOrEmpty(this.txtStartDate.Text))
{
}
else
{
cmd2.Parameters.Add("#StDate", SqlDbType.Date).Value = txtStartDate.Text;
}
if (string.IsNullOrEmpty(this.txtEndDate.Text))
{
}
else
{
cmd2.Parameters.Add("#EnDate", SqlDbType.Date).Value = txtEndDate.Text;
}
if (MyRptSproc != "")
{
connection2.Open();
cmd2.ExecuteNonQuery();
}
try
{
//Now open the View and bind it to the GridView
string SelectView = "SELECT * FROM " + MySQLView + "";
SqlConnection con = new SqlConnection(str2);
SqlCommand SelectCmd = new SqlCommand(SelectView, con);
SqlDataAdapter SelectAdapter = new SqlDataAdapter(SelectCmd);
//Fill the dataset
DataSet RunReport = new DataSet();
SelectAdapter.Fill(RunReport);
GridView_Reports.DataSource = RunReport;
GridView_Reports.DataBind();
}
catch
{
ScriptManager.RegisterStartupScript(btnSubmit, typeof(Button), "Report Menu", "alert('There is no View associated with this report.\\nPlease contact the developers and let them know of this issue.')", true);
return;
}
}
}
}
}
The Problem: When the code hits the line
cmd2.ExecuteNonQuery();
and there is a start and end date entered, it's telling me "Procedure or function expects parameter '#StDate', which is not supplied." I've stepped through the code and see that cmd2 has 2 parameters, so why isn't the function seeing them?
Additionally, here's the specific stored procedure which is causing the snafu (I've got 2 others that run fine, but neither of them are trying to pass parameters to a stored procedure:
ALTER procedure [dbo].[usp_DailyProc]
#StDate smalldatetime,
#EnDate smalldatetime
AS
BEGIN
IF OBJECT_ID('Temp_DailyProduction') IS NOT NULL
drop table Temp_DailyProduction;
IF OBJECT_ID('Temp_AuditorDailyProduction') IS NOT NULL
drop table Temp_AuditorDailyProduction;
SELECT
[Audit Date],
Auditor,
Count([Doc #]) AS [Claim Count],
Count([Primary Error Code]) AS [Final Error],
SUM(case when [Status]='removed' then 1 else 0 end) as Removed,
SOCNUM
INTO Temp_DailyProc
FROM PreClosed
WHERE (((Get_Next_Status)='Closed' Or (Get_Next_Status)='Panel' Or (Get_Next_Status)='HPanel'))
GROUP BY [Audit Date], Auditor, SOCNUM
HAVING ((([Audit Date]) Between #StDate And #EnDate));
SELECT
TDP.[Audit Date],
TDP.Auditor,
EID.EMPLOYEE AS [Auditor Name],
TDP.[Claim Count],
TDP.[Final Error],
TDP.Removed,
TDP.[Removed]/TDP.[Final Error] AS [Error Removal Ratio],
TDP.SOCNUM
INTO Temp_AuditorDailyProc
FROM Temp_DailyProc TDP
LEFT JOIN PreLookup EID
ON TDP.Auditor = EID.ID_Trim;
drop table Temp_DailyProduction;
END
I think you need to use the AddWithValue method instead of the Add method.
AddWithValue replaces the SqlParameterCollection.Add method that takes
a String and an Object. The overload of Add that takes a string and an
object was deprecated because of possible ambiguity with the
SqlParameterCollection.Add overload that takes a String and a
SqlDbType enumeration value where passing an integer with the string
could be interpreted as being either the parameter value or the
corresponding SqlDbType value. Use AddWithValue whenever you want to
add a parameter by specifying its name and value.
Had another thought, you are passing a string (Text) value as Date parameter. I think you should convert this to a date type. e.g.
cmd2.Parameters.Add("#StDate", SqlDbType.Date).Value = DateTime.Parse(txtStartDate.Text);
A more robust way of doing this would be to use DateTime.TryParseExact.

Categories