I am somwhat new to SQL, so I am not sure I am going about this the right way.
I am trying to fetch data from my SQL Server database where I want to find out if checkedin is 1/0, but it needs to search on a specific user and sort after the newest date as well.
What I am trying to do is something like this:
string connectionString = ".....";
SqlConnection cnn = new SqlConnection(connectionString);
SqlCommand checkForInOrOut = new SqlCommand("SELECT CHECKEDIN from timereg ORDER BY TIME DESC LIMIT 1 WHERE UNILOGIN = '" + publiclasses.unilogin + "'", cnn);
So my question, am I doing this right? And how do I fetch the data collected, if everything was handled correctly it should return 1 or 0. Should I use some sort of SqlDataReader? I am doing this in C#/WPF
Thanks
using (SqlDataReader myReader = checkForInOrOut.ExecuteReader())
{
while (myReader.Read())
{
string value = myReader["COLUMN NAME"].ToString();
}
}
This is how you would read data from SQL, but i recommend you looking into Parameters.AddWithValue
There are some errors in your query. First WHERE goes before ORDER BY and LIMIT is an MySql keyword while you are using the Sql Server classes. So you should use TOP value instead.
int checkedIn = 0;
string cmdText = #"SELECT TOP 1 CHECKEDIN from timereg
WHERE UNILOGIN = #unilogin
ORDER BY TIME DESC";
string connectionString = ".....";
using(SqlConnection cnn = new SqlConnection(connectionString))
using(SqlCommand checkForInOrOut = new SqlCommand(cmdText, cnn))
{
cnn.Open();
checkForInOrOut.Parameters.Add("#unilogin", SqlDbType.NVarChar).Value = publiclasses.unilogin;
// You return just one row and one column,
// so the best method to use is ExecuteScalar
object result = checkForInOrOut.ExecuteScalar();
// ExecuteScalar returns null if there is no match for your where condition
if(result != null)
{
MessageBox.Show("Login OK");
// Now convert the result variable to the exact datatype
// expected for checkedin, here I suppose you want an integer
checkedIN = Convert.ToInt32(result);
.....
}
else
MessageBox.Show("Login Failed");
}
Note how I have replaced your string concatenation with a proper use of parameters to avoid parsing problems and sql injection hacks. Finally every disposable object (connection in particular) should go inside a using block
Related
I am developing a website using ASP.NET C#. I have an SQL-Server database. I want to be able to retrieve data from the table with my data already in it.
For example, Here is my Details Table. I want to be able to do something like SELECT SteamName FROM Details WHERE SteamID = #SteamID and return the value that I get from the query to a C# object.
Here is the C# I have tried:
private void ReadSteamDetails()
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = "SELECT SteamID FROM Details WHERE SteamID = #SteamID";
command.Parameters.Add("#SteamID", SqlDbType.NVarChar);
command.Parameters["#SteamID"].Value = SteamID;
connection.Open();
DisplaySQLID = command.ExecuteNonQuery().ToString();
connection.Close();
}
}
}
Running this code simply returns -1
You are using ExecuteNonQuery(), that returns an int value indicating the number of rows effected by the SQL statement. It should be used with insert, update or delete, but not with select statement.
When executing a select statement, you should use either ExecuteReader() if you want to iterate the query result using a DataReader, or use a DataAdapter to fill a DataSet or a DataTable, or use ExecuteScalar() if your query should return 0 or 1 results (and that's the case here).
Also, your code can and should be shorter - for instance, you can specify the select statement and connection object directly in the SqlCommand constructor.
Here is how I would write it:
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand("SELECT SteamID FROM Details WHERE SteamID = #SteamID", connection))
{
command.Parameters.Add("#SteamID", SqlDbType.NVarChar).Value = SteamID;
connection.Open();
DisplaySQLID = command.ExecuteSalar()?.ToString() ?? "";
}
}
Note that your query might not return any result so the ExecuteScalar() will return null, hence the use if the null conditional operator (?.) and the null coalescing operator (??).
For that exact query use ExecuteScalar instead of ExecuteNonQuery.
you can use the Entity Framework for that purpose. . .
with the combination of Entity Framework and link u can get your desire result set .
Entities entities = new Entities(Connection-String);
var result = entities.Details.Where(a=>a.SteamID = #SteamID )
Would you please show me how to retrieve data from SQL Server to C# (Windows Forms application)?
Consider I have a textbox and I need to fill it with data from SQL Server WHERE 'emp_id = something' for example, how can I do it without a DataGridView?
Or take this another example:
SELECT sum(column) FROM table_name
How to get the value of the above command (also without a DataGridView)?
There are multiple ways to achieve this. You can use DataReader or DataSet \ DataTable. These are connected and disconnected architectures respectively. You can also use ExecuteScalar if you want to retrieve just one value.
Recommendations:
Enclose SqlConnection (and any other IDisposable object) in using block. My code uses try-catch block.
Always use parameterized queries.
Following is some example code with DataReader in case your query returns multiple rows. The code is copied from here.
//Declare the SqlDataReader
SqlDataReader rdr = null;
//Create connection
SqlConnection conn = new SqlConnection("Your connection string");
//Create command
SqlCommand cmd = new SqlCommand("Your sql statement", conn);
try
{
//Open the connection
conn.Open();
// 1. get an instance of the SqlDataReader
rdr = cmd.ExecuteReader();
while(rdr.Read())
{
// get the results of each column
string field1 = (string)rdr["YourField1"];
string field2 = (string)rdr["YourField2"];
}
}
finally
{
// 3. close the reader
if(rdr != null)
{
rdr.Close();
}
// close the connection
if(conn != null)
{
conn.Close();
}
}
In case your query returns single value, you can continue with above code except SqlDataReader. Use int count = cmd.ExecuteScalar();. Please note that ExecuteScalar may return null; so you should take additional precautions.
Filling a Textbox:
using (var sqlConnection = new SqlConnection("your_connectionstring"))
{
sqlConnection.Open();
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = "select sum(field) from your_table";
object result = sqlCommand.ExecuteScalar();
textBox1.Text = result == null ? "0" : result.ToString();
}
sqlConnection.Close();
}
for reading more than one row you can take a look at SqlCommand.ExecuteReader()
You need to use direct database access such as in the System.Data.SqlClient Namespace which is documented here System.Data.SqlClient Namespace.
Basically, look up creating a SQLConnection and SQLCommand and using them to retrieve the data.
When running the below method in my application the app freezes and when I pause VS it seems to be stuck on the line that goes:
SqlDataReader reader = select.ExecuteReader();
I've got other SQL methods running fine so I know the connection string is correct, I've double checked the SQL and that's fine. Am I wrong in think the reader variable can not contain the returning value of the scalar function when the ExecuteReader() is called?
public static bool AccountValidation(string username, string password)
{
string statement = "select dbo.AccountValidation('" + username + "','" + password + "')";
SqlCommand select = new SqlCommand(statement, connect);
connect.Open();
SqlDataReader reader = select.ExecuteReader();
string result = reader.ToString();
connect.Close();
if (result != "true")
{
return false;
}
else
{
return true;
}
}
The main problem is that you are not actually reading anything back from the data reader, you have to iterate over the result set and then read based on ordinal/positional index.
There are also other big problems like
Not using parameters which leaves the code vulnerable to sql injection attacks.
Not wrapping your disposables in using blocks which could leave database connections open if there are exceptions
sharing db connections in your types. Create connections on an as needed basis and then dispose them when you are done.
Here is your updated code with fixes. I guessed at the column types (varchar), fix that and the lengths as they are implemented in your schema.
public static bool AccountValidation(string username, string password)
{
const string statement = "select dbo.AccountValidation(#username, #password)";
string result = null;
// reference assembly System.Configuration
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["YourDb"].ConnectionString;
using(var connection = new SqlConnection(connStr))
using(SqlCommand cmd = new SqlCommand(statement, connect))
{
cmd.Parameters.Add(new SqlParameter("#username", SqlDbType.VarChar, 200){Value = username});
cmd.Parameters.Add(new SqlParameter("#password", SqlDbType.VarChar, 200){Value = password});
connect.Open();
using(SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.Read())
result = reader.GetString(0); // read back the first column of the first row
}
}
if (result != "true")
{
return false;
}
else
{
return true;
}
}
On a side note it would be cleaner to return a bit from your database function AccountValidation and then read that back with reader.GetBoolean(0) and assign that to the result and return that directly instead of doing string comparisons.
Also, as mentioned above in the comments, if you are only returning 1 value it is easier (and less code) to call ExecuteScalar instead of ExecuteReader.
Add the line
reader.Read();
before the line
string result = reader.ToString();
Also, please parameterize your queries.
I don't have enough reputation to leave a comment, but to answer part of your question, yes, you can use a SqlDataReader to read single scalar results with or without column aliases.
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() + "'";
I'm running a query from a web form to update records. Since I'm just learning about C#, I'm using a command string as opposed to a stored procedure.
My update method is as follows:
public void updateOne()
{
string commandText = "update INVOICE SET <Redacted> = #<Redacted>,
Supplier = #Sup, SupplierName = #SupN, NetTotal = #Net,
VATTotal = #VAT, InvoiceDate = #InvDt "
<needed a line break here, which is why I split the string again>
+ "WHERE K_INVOICE = #K_INV";
using (SqlConnection dbConnection = new SqlConnection
(conParams.connectionString))
{
SqlCommand cmd = new SqlCommand(commandText, dbConnection);
cmd.Parameters.Add("#K_INV", SqlDbType.Int);
cmd.Parameters["#K_INV"].Value = #K_INV;
cmd.Parameters.AddWithValue("#<Redacted>", #<Redacted>.ToString());
cmd.Parameters.AddWithValue("#Sup", #Sup.ToString());
cmd.Parameters.AddWithValue("#SupN", #SupN.ToString());
cmd.Parameters.AddWithValue("#Net", #Net.ToString());
cmd.Parameters.AddWithValue("VAT", #VAT.ToString());
cmd.Parameters.AddWithValue("#InvDt", #InvDt.ToString());
try
{
dbConnection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
errorString = e.Message.ToString();
}
}
}
Catch stalls on an SQL error (Incorrect syntax near SET), and I have an idea that the issue occurs because I convert the parameters to strings. The first parameter is an Int, which should be OK.
If this is the case, what should I convert the parameters to? If not, what on earth is wrong?
Try to add a # before the string to escape the breaklines, for sample:
string commandText = #"update INVOICE SET [Redacted] = #Redacted,
Supplier = #Sup, SupplierName = #SupN, NetTotal = #Net,
VATTotal = #VAT, InvoiceDate = #InvDt "
+ "WHERE K_INVOICE = #K_INV";
In parameterName argument you can add the # but the value not, just the variable, for sample
cmd.Parameters.AddWithValue("#Redacted", redacted.ToString());
Try to execute this query in the databse with some values to check if everything is correct. You could use [brackets] in the table name and column names if you have a reserved word.
I would recommend you read this blog article on the dangers of .AddWithValue():
Can we stop using AddWithValue already?
Instead of
cmd.Parameters.AddWithValue("#Sup", #Sup.ToString());
you should use
cmd.Parameters.Add("#Sup", SqlDbType.VarChar, 50).Value = ...(provide value here)..;
(is your variable in C# really called #SupN ?? Rather unusual and confusing....)
I would recommend to always define an explicit length for any string parameters you define