C# - Select query not working in SqlCommand - c#

I have to select user name from a database in SQL Server. The query that is generated by SqlCommand works in SQL Server Management Studio but not in my code.
And this only happens when the input is like AFFAQPC/affaq containing /.
The code is:
public int? getid()
{
SqlConnection Db = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
Db.Open();
// Searching for id in Users table from the logged in username
SqlCommand command = new SqlCommand("SELECT TOP 1 id FROM UsersLogin where username = '#user1';", Db);
command.Parameters.AddWithValue("#user1", userName);
string query = command.CommandText;
foreach (SqlParameter p in command.Parameters)
{
query = query.Replace(p.ParameterName, p.Value.ToString());
}
Trace.WriteLine(query);
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Trace.WriteLine("111");
int id = Convert.ToInt32(reader["id"]);
Trace.WriteLine(id);
Db.Close();
return id;
}
}
Db.Close();
return null;
}
The error occurs when input contains a /.
The query that is generated in SqlCommand:
SELECT TOP 1 id
FROM UsersLogin
WHERE username = 'AFFAQPC\affaq';

remove the single quotes from your query string
username = '#user1'
to
username = #user1
The "Parameter" should be WITHOUT the "#", just the name
command.Parameters.AddWithValue("user1", userName);
if the userName value is a string (confirming whatever your source is), that will be properly recognized when processed. You do not need to explicitly quote-it. Otherwise, the query is specifically looking for a user '#user1' which is probably why it is not returning what you expect.
I have NO idea why you are cycling through all parameters to assign the value... the userName field should already be good to go without doing your foreach parameter check.

Related

Get data from SQL table to empty variable

I'm trying to search in table(called Accounts) for Username and in the line of this user in coulmn 4 that called PhotoId there is string that i need to get and add it to empty string value that i made before. thats what i tryied
i want to do it by Query like my code
string ecid = e.CallbackQuery.Message.Chat.Id.ToString();
using (SqlCommand sqlCommand = new SqlCommand("SELECT * from Accounts where Username like #username", con))
{
con.Open();
sqlCommand.Parameters.AddWithValue("#username", ecid);
string variable;
string userfound = (string)sqlCommand.ExecuteScalar();
con.Close();
if (userfound == ecid)
{
using (SqlCommand GotPhoto = new SqlCommand("SELECT PhotoId from Accounts where Username like #user", con))
{
con.Open();
GotPhoto.Parameters.AddWithValue("#user", ecid);
DataPid = GotPhoto.ExecuteScalarAsync().ToString();
con.Close();
// await bot.SendPhotoAsync("xxx", DataPid, capo, parseMode: ParseMode.Markdown, replyMarkup: zz);
await bot.SendTextMessageAsync(cid, "its been Poster", parseMode: ParseMode.Html);
}
}
else
{
MessageBox.Show("Not Working");
}
}
You are combining a SELECT * ... with ExecuteScalar(), which "returns the value of the first column in the first row of the resultset. Additional rows and columns are ignored."(https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar(v=vs.110).aspx). Probably username is not the first column in the accounts table, so the content of another column is retured and your comparison fails.
Use SELECT username from Accounts where Username like #username instead.
EDIT
Depending on your data-model you can also combine the two queries into one as you are querying the same row twice.
con.Open();
using (var cmd = new SqlCommand("SELECT PhotoId from Accounts where Username like #user", con)) {
cmd.Parameters.AddWithValue("#user", ecid);
using (var reader = await cmd.ExecuteReaderAsync()) {
if (!reader.HasRows || ! reader.Read()) {
MessageBox.Show("User not found");
} else {
DataPid = cmd.GetYOURDATATYPE(0).ToString();
//....
}
}
}
con.Close();
According to the documentation of ExecuteScalar =>
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
When you perform string userfound = (string)sqlCommand.ExecuteScalar();
You are comparing the username to a value ( but wich one ? the value of the first column in the create table instruction)
I Suggest you change the first query to be sure to select username
PS:
To be consistant, you can also rename both of your query parameter #username instead of #user and #username because it refers exactly to the same thing.
You can perform only one query instead of two. ( select photoid ... where username = .. , and just check if the result contains one row )

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 + "';

ASP.NET, C# How to Pass a StringQuery to a custom SQL Command

I have a slight issue, I have a ASP.NET Webforms application. I'm sending over a url?id=X were X is my database index or id.
I have a C# class file to run my SQL connection and query. Here is the code:
public DataTable ViewProduct(string id)
{
try
{
string cmdStr = "SELECT * Products WHERE Idx_ProductId = " + id;
DBOps dbops = new DBOps();
DataTable vpTbl = dbops.RetrieveTable(cmdStr, ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
return vpTbl;
}
catch (Exception e)
{
return null;
}
}
So as you can see my problem lies within string cmdStr = "SQL Query" + variable;
I'm passing over my index or id through the URL then requesting it and turning it into a string then using ViewProduct(productId).
I don't know what syntax or how to add the id into my C# string sql query. I've tried:
string cmdStr = "SELECT * Products WHERE Idx_ProductId = #0" + id;
string cmdStr = "SELECT * Products WHERE Idx_ProductId = {0}" + id;
also what I have currently to no avail.
I was so sure this would be a duplicate of some canonical question about parameterized queries in C#, but apparently there isn't one (see this)!
You should parameterize your query - if you don't, you run the risk of a malicious piece of code injecting itself into your query. For example, if your current code could run against the database, it would be trivial to make that code do something like this:
// string id = "1 OR 1=1"
"SELECT * Products WHERE Idx_ProductId = 1 OR 1=1" // will return all product rows
// string id = "NULL; SELECT * FROM UserPasswords" - return contents of another table
// string id = "NULL; DROP TABLE Products" - uh oh
// etc....
ADO.NET provides very simple functionality to parameterize your queries, and your DBOps class most assuredly is not using it (you're passing in a built up command string). Instead you should do something like this:
public DataTable ViewProduct(string id)
{
try
{
string connStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
// #id is very important here!
// this should really be refactored - SELECT * is a bad idea
// someone might add or remove a column you expect, or change the order of columns at some point
cmd.CommandText = "SELECT * Products WHERE Idx_ProductId = #id";
// this will properly escape/prevent malicious versions of id
// use the correct type - if it's int, SqlDbType.Int, etc.
cmd.Parameters.Add("#id", SqlDbType.Varchar).Value = id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable vpTbl = new DataTable();
vpTbl.Load(reader);
return vpTbl;
}
}
}
}
catch (Exception e)
{
// do some meaningful logging, possibly "throw;" exception - don't just return null!
// callers won't know why null got returned - because there are no rows? because the connection couldn't be made to the database? because of something else?
}
}
Now, if someone tries to pass "NULL; SELECT * FROM SensitiveData", it will be properly parameterized. ADO.NET/Sql Server will convert this to:
DECLARE #id VARCHAR(100) = 'NULL; SELECT * FROM SensitiveData';
SELECT * FROM PRoducts WHERE Idx_ProductId = #id;
which will return no results (unless you have a Idx_ProductId that actually is that string) instead of returning the results of the second SELECT.
Some additional reading:
https://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon
Difference between Parameters.Add and Parameters.AddWithValue
SQL injection on INSERT
Avoiding SQL injection without parameters
How do I create a parameterized SQL query? Why Should I? (VB.NET)
How can I prevent SQL injection in PHP? (PHP specific, but many helpful points)
Is there a canonical question telling people why they should use SQL parameters?
What type Products.Idx_ProductId is?
Probably it is string, than you need to use quotes: "... = '" + id.Trim() + "'";

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();

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