I want to get a specific value out of my Database.
To do so I wrote a function that I call like that:
strUserId = SqlSelectToString("SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName");
The function works like that:
private string SqlSelectToString(string strSqlCommand)
{
string result = "";
using (SqlConnection connection = new SqlConnection(strConnectionString))
{
using (SqlCommand command = new SqlCommand(strSqlCommand, connection))
{
command.Parameters.AddWithValue("#UserName", strUserName);
try
{
connection.Open();
result = (string)command.ExecuteScalar();
}
catch
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
}
}
}
return result;
I tried many different approaches but wasn't able to add "strUserName" to the query.
The query-string remained always "SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName".
What am I doing wrong?
Thanks in advance.
EDIT:
The Username of whom we need the UserId is always the same and defined by the Webserver-Admin through the Web.config as follows:
<appSettings>
<add key="UserName" value="kunde"/>
<add key="ApplicationName" value="InternalTest"/>
before the method SqlSelectToString is executed I fetch the strUsername from the config like that:
strUserName = System.Configuration.ConfigurationManager.AppSettings["UserName"].ToString();
Hope that helps.
EDIT:
Finally found a solution to the problem.
This is the right approach:
private const string strSelectUserId = "SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName";
/// <summary>
/// Saves the result of an SQL-Query in a string
/// </summary>
/// <param name="strSqlCommand"></param>
private string SqlSelectToString(string strSqlCommand)
{
string result = "";
SqlConnection connection = new SqlConnection(strConnectionString);
SqlCommand command = new SqlCommand(strSqlCommand, connection);
command.Parameters.AddWithValue("#UserName", strUserName);
command.Parameters.AddWithValue("#ApplicationName", strApplicationName);
command.Parameters.AddWithValue("#ProjectId", strProjectId);
SqlDataReader reader;
try
{
connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
result = reader[0].ToString();
}
}
catch
{
bInsert = false;
}
finally
{
if (connection != null)
{
connection.Close();
connection.Dispose();
}
}
return result;
}
The query-string remained always "SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName".
This is what is supposed to happen. It's the whole point of parameterized queries. The parameter value is never substituted directly into the sql command, and thus any possibility of an sql injection attack is avoided. Instead, it's more as if you executed sql code like this:
declare #UserName nvarchar(50);
set #UserName = '... magic here to populate this without actually building sql code';
SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName
(Though for Sql Server it actually relies on sp_executesql)
I'd help you more, but you never tell us what actually happens after you run the code... and this is likely because you swallow any exception that might tell you what went wrong. The code you have is almost certainly throwing an exception that would tell you exactly what's going wrong here, and you never get to see it.
Seriously, just remove the try/catch from that code entirely. You don't need it at all, and the entire catch block is just extra code.
One more thing before I go: AddWithValue() isn't the best option for your query parameters.
Put all that together, you end up with a helper method that looks more like this:
private string GetSqlScalar(string strSqlCommand, params SqlParameter[] parameters)
{
string result = "";
using (SqlConnection connection = new SqlConnection(strConnectionString))
using (SqlCommand command = new SqlCommand(strSqlCommand, connection))
{
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
connection.Open();
result = (string)command.ExecuteScalar();
}
return result;
}
And then call it like this:
strUserId = GetSqlScalar("SELECT UserId FROM BugNet.dbo.Users WHERE UserName = #UserName",
new SqlParameter("#UserName", SqlDbType.NVarChar, 50) {Value = strUserNanme });
(Of course using the correct parameter type and length from the database).
Related
I have the following code that fails at the if (!rdr.Read()), and I can't see what I am doing wrong. When I look in the database using the value in fullPath, the record exists. Here's my code:
Song song = new Song();
connectionManager = new ConnectionManager();
try
{
using (SqlConnection conn = new SqlConnection(connectionManager.ConnectionString))
{
conn.Open();
string query = $"SELECT * FROM Songs WHERE FullPath LIKE '#FullPath%'";
using (SqlCommand queryString = new SqlCommand(query, conn))
{
queryString.Parameters.Add("#FullPath", SqlDbType.NVarChar, 300).Value = fullPath;
SqlDataReader rdr = queryString.ExecuteReader();
if (!rdr.Read())
{
throw new InvalidOperationException("No records were returned.");
}
song.Title = rdr["Title"].ToString();
song.Artist = rdr["Artist"].ToString();
song.Genre = rdr["Genre"].ToString();
song.Album = rdr["Album"].ToString();
song.Year = (uint)rdr["Year"];
song.Length = rdr["Length"].ToString();
song.FullPath = rdr["FullPath"].ToString();
}
conn.Close();
}
}
catch (Exception ex)
{
Logger.log.Error($"Error getting song: {fullPath}\n", ex);
}
return song;
The #FullPath placeholder for the parameter is not included in the SQL string correctly. When you use parameters, you do not put them in single-quotes. You want this:
string query = "SELECT * FROM Songs WHERE FullPath LIKE #FullPath + '%'";
Alternatively, you could do this:
string query = "SELECT * FROM Songs WHERE FullPath LIKE #FullPath";
// ...
queryString.Parameters.Add("#FullPath", SqlDbType.NVarChar, 300).Value = fullPath + "%";
Remember, parameterized queries are more than a simple sanitization + string substitution. Rather, it quarantines the data inside a variable. Therefore the SQL code must treat the placeholder as an SQL variable. What you had was just a string literal where the value happened to match the parameter name.
That's as far as I got. There may be other errors, too. If you want better help, post the actual error message.
I'm trying to get my LoginButton to work, it isn't really doing what I want it to do.
I already have a RegisterButton which works perfectly and creates the account without any problems, but when trying to do my LoginButton it connects to the database but doesn't really check if the account exists using selectQuery and it should change WarningLabel.Text to "Wrong Name or Password". it does go through the first try and changes the WarningLabel.Text to "Welcome " + NameInput.Text;
private void LoginButton_Click(object sender, System.EventArgs e)
{
string selectQuery = $"SELECT * FROM bank.user WHERE Name='{NameInput.Text}' AND Password='{GetHashString(PasswordInput.Text)}';";
MySqlCommand cmd;
connection.Open();
cmd = new MySqlCommand(selectQuery, connection);
try
{
cmd.ExecuteNonQuery();
WarningLabel.Text = "Welcome " + NameInput.Text;
} catch
{
WarningLabel.Text = "Wrong Name or Password";
}
connection.Close();
}
Best Regards - Nebula.exe
The ExecuteNonQuery is not intented to be used with SQL statements that return data, you should use ExecuteReader or ExecuteScalar, you can check the MySqlCommand.ExecuteReader documentation
Warning: Your code does have a SQL Injection vulnerability in this part of the SQL statement Name='{NameInput.Text}' Check this SQL Injection explanation
Usage example (from the documentation, slightly modified):
using (MySqlConnection myConnection = new MySqlConnection(connStr))
{
using (MySqlCommand myCommand = new MySqlCommand(mySelectQuery, myConnection))
{
myConnection.Open();
MySqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
}
}
}
You should check if there are records returned. cmd.ExecuteNonQuery(); won't tell you if records are returned because it will just execute the query. You should use ExecuteScalar or a MySQL Data Reader ExecuteReader and track the results.
Note : Your code is prone to SQL Injections, you might want to use Parameters in your query like #name and #password.
Your Query goes something like this.
string selectQuery = $"SELECT IFNULL(COUNT(*),0) FROM bank.user WHERE Name=#name AND Password=#password;";
Then use parameters
cmd.parameters.AddWithValue(#name, NameInput.Text);
cmd.parameters.AddWithValue(#password, GetHashString(PasswordInput.Text));
Then verify if the query returns result
If cmd.ExecuteScalar() > 0
//If count is > 0 then Welcome
//Else Wrong username or password
End If
Your life, made easy:
private void LoginButton_Click(object sender, System.EventArgs e)
{
var cmd = "SELECT * FROM bank.user WHERE Name=#name AND Password=#pw";
using var da = new MySqlDataAdapter(cmd, connection);
da.SelectCommand.Parameters.AddWithValue("#name", NameInput.Text);
da.SelectCommand.Parameters.AddWithValue("#pw",GetHashString(PasswordInput.Text));
var dt = new DataTable();
da.Fill(dt);
if(dt.Rows.Count == 0)
WarningLabel.Text = "Wrong Name or Password";
else
WarningLabel.Text = $"Welcome {dt.Rows[0]["FullName"]}, your last login was at {dt.Rows[0]["LastLoginDate"]}";
}
Your life, made easier (with Dapper):
class User{
public string Name {get;set;} //username e.g. fluffybunny666
public string FullName {get;set;} //like John Smith
public string Password {get;set;} //hashed
public DateTime LastLoginDate {get;set;}
}
//or you could use a record for less finger wear
record User(string Name, string FullName, string Password, DateTime LastLoginDate);
...
using var c = new MySqlConnection(connection):
var u = await c.QuerySingleOrDefaultAsync(
"SELECT * FROM bank.user WHERE Name=#N AND Password=#P",
new { N = NameInput.Text, P = GetHashString(PasswordInput.Text)}
);
if(u == default)
WarningLabel.Text = "Wrong Name or Password";
else
WarningLabel.Text = $"Welcome {u.FullName}, your last login was at u.LastLoginDate";
I am having an issue with my c# code. I am trying to check if a username exists already so I have a select statement but when I breakpoint it, it leaves the code right after I assign the reader. Here is my code:
SqlConnection conn = new SqlConnection(Properties.Settings.Default.CategoriesConnectionString);
SqlCommand chkUser = new SqlCommand("SELECT [Username] FROM [Accounts] WHERE [Username] = #username", conn);
chkUser.Parameters.AddWithValue("#username", txtUsername.Text);
conn.Open();
SqlDataReader sqlReader = chkUser.ExecuteReader(); //leaves code right here
if (sqlReader.HasRows)
{
MessageBox.Show("That username already exists. Please choose another.");
txtUsername.Focus();
return;
}
conn.Close();
I figure it is because there is nothing in the table yet but I don't know why it is not checking whether or not it has rows and is just leaving.
Any help is appreciated. Thanks
Some more information on the issue would be useful. Seems like you are getting an exception for some reason (as #Guffa said) and without any further details it becomes difficult guessing what the reason is. Try changing the code you posted to the following:
using(SqlConnection conn = new SqlConnection(Properties.Settings.Default.CategoriesConnectionString))
using(SqlCommand chkUser = new SqlCommand("SELECT [Username] FROM [Accounts] WHERE [Username] = #username", conn))
{
chkUser.Parameters.AddWithValue("#username", txtUsername.Text);
try
{
conn.Open();
using(SqlDataReader sqlReader = chkUser.ExecuteReader())
{
if (sqlReader.HasRows)
{
MessageBox.Show("That username already exists. Please choose another.");
txtUsername.Focus();
return;
}
}
}
catch(Exception e)
{
// manage exception
}
}
and see if something changes. In case it doesn't try debugging and see what kind of exception it throws.
Here is an example that i use in a login scenario where i've stored usernames in a mysql database table.
Even though i use MySQL there shouldnt be much of a difference.(not sure about this though).
public static string CheckUsername()
{
bool usernameCheck = false;
InitiateDatabase();//contains the root.Connection string
MySqlCommand readCommand = rootConnection.CreateCommand();
readCommand.CommandText = String.Format("SELECT username FROM `SCHEMA`.`USERTABLE`");
rootConnection.Open();
MySqlDataReader Reader = readCommand.ExecuteReader();
while (Reader.Read())
{
for (int i = 0; i < Reader.FieldCount; i++)
{
if (Reader.GetValue(i).ToString() == USERNAME_STRING)
{
usernameCheck = true;
}
}
}
rootConnection.Close();
if (usernameCheck)
{
return "Succes";
}
else
{
return "Wrong Username!";
}
}
This is of course without exception handling, which you might find useful during testing and if its meant to be used by others.
I'm developing a C# solution with data access to Oracle.
And would like to have a generic solution about query.
Here is a part of my code :
public DataTable GetData(string query)
{
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OracleClient");
using (DbConnection conn = factory.CreateConnection())
{
try
{
DbConnectionStringBuilder csb = factory.CreateConnectionStringBuilder();
csb["Data Source"] = #"Northwind";
csb["User Id"] = #"Northwind";
csb["Password"] = #"Northwind";
conn.ConnectionString = csb.ConnectionString;
conn.Open();
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = query;
using (DataTable dt = new DataTable())
{
DbDataAdapter da = factory.CreateDataAdapter();
cmd.CommandType = CommandType.Text;
da.SelectCommand = cmd;
da.Fill(dt);
return dt;
}
}
}
catch (Exception ex)
{
throw new Exception("Error", ex);
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
And I call my method like this :
DataAccess.Provider data = new DataAccess.Provider();
DataTabel dt = dt.GetData("select * from myTable);
This works pretty good but this is not my aim.
I have a second class called CL_mpg with all my SQL queries.
class CL_MPG
{
public string rq_sql;
public string selectParam(string param)
{
this.rq_sql = "select * from myTable where id = '" + param + "';";
return this.rq_sql;
}
public string select()
{
this.rq_sql = "select * from myTable";
return this.rq_sql;
}
//...
}
And I would like to use my methods selectParam and/or select to fill my datatable, but I don't know how to do that.
Although others complain at your learning attempt, everyone has to start somewhere. Your method is actually an ok start, but I would change the parameter from a string to a DbCommand object. Then, you can create your methods to properly build the command and set proper parameters. Then pass the entire prepared command to your wrapper method (that creates connection, tests open successful, queries data, etc) and have your method return a DataTable object as you have... something like
public class CL_MPG
{
private DataTable GetData(DbCommand cmd )
{
// do all the same as you have with exception of your USING DBCOMMAND.
// just set the connection property of the incoming command to that of
// your connection created
// AT THIS PART --
// using (DbCommand cmd = conn.CreateCommand())
// {
// cmd.CommandText = query;
// just change to below and remove the closing curly bracket for using dbcommand
cmd.Connection = conn;
}
// Now, your generic methods that you want to expose for querying
// something like
public DataTable GetAllData()
{
DbCommand cmd = new DbCommand( "select * from YourTable" );
return GetData( cmd );
}
public DataTable GetUser( int someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where ID = #parmID" );
cmd.Parameters.Add( "#parmID", someIDParameter );
return GetData( cmd );
}
public DataTable FindByLastName( string someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where LastName like #parmTest" );
cmd.Parameters.Add( "#parmTest", someIDParameter );
return GetData( cmd );
}
}
Notice the command is being built and fully prepared and parameterized vs concatination of strings as prior comment was made which could expose you to SQL-injection. As for the parameters, and not querying Oracle, they may need to be tweaked some. Different engines use slightly different conventions. If connecting to SQL-Server database, it uses "#" to identify a parameter. In SyBase Advantage Database, it uses ":". Using Visual FoxPro, a simple "?" placeholder is used.
Also, if your query has many criteria, just keep adding additional "#parm" type placeholders, then add your parameters in the same order as they appear in your query just to make sure you didn't miss any. Some functions could have none, one or more based on your needs. Then, in the samples provided, its as simple as doing something like
DataTable whoIs = yourCL_MPGObject.GetUser( 23 );
if( whoIs.Rows.Count > 0 )
MessageBox.Show( whoIs.Rows[0]["WhateverColumnName"] );
I am trying to update a single value into my access .accdb database via a c# winform interface. my SQL statement is:
updateString("UPDATE Password_Table SET Password = '" + confirmnewpasswordTextBox.Text + "' WHERE Password_ID = 'user'");
field-wise it should be correct but whenever i execute the updateString function it only returns zero. may I know what I am doing wrongly in the following example?
public static bool updateString(string SQL)
{
using (var connection = new OleDbConnection(connectionString))
using (var command = connection.CreateCommand())
{
connection.Open();
command.CommandText = SQL;
command.CommandType = CommandType.Text;
try
{
return command.ExecuteNonQuery();
}
catch
{
return -1;//for error
}
}
}
Thank you!!
update:
System.Data.OleDb.OleDbException: Syntax error in UPDATE statement.
hmmm, i still cant figure out what is wrong, my table is Password_Table, and I am trying to update a column called Password where the Password_ID is "user".
update: found the error! turns out that Password is like a restricted keyword and i had to cover it in [ ] before it could work..
There are serious issues with your code. It is vulnerable to SQL injection. You should always use parametrized queries to avoid that. For example:
public static string UpdatePassword(string user, string password)
{
using (var connection = new OleDbConnection(connectionString))
using (var command = connection.CreateCommand())
{
connection.Open();
command.CommandText = "UPDATE Password_Table SET Password = #pwd WHERE Password_ID = #user";
command.Parameters.AddWithValue("#pwd", password);
command.Parameters.AddWithValue("#user", user);
try
{
return command.ExecuteNonQuery();
}
catch
{
return -1;//for error
}
}
}
And then invoke like this:
int rowsAffetected = UpdatePassword("user", confirmnewpasswordTextBox.Text);
Now, if this returns 0 it means that there is no record in your database which matches the Password_ID = user condition and there is nothing to update.