I have Sqlite method that makes SELECT query:
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
SQLiteDataReader reader = sqCommand.ExecuteReader();
return reader.GetString(0);
}
} catch (Exception e) {
// do exception handling
}
I tried to get last inserted id:
sql = 'SELECT id FROM Pacients ORDER BY id DESC LIMIT 1';
I tried to di that like:
return reader.GetString(0);
it's throwing me down on exception "No current row"
After calling ExecuteReader you need to call Read to position on the first record of the dataset. Read returns true/false to inform you if there are records to read. So your code changes to
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
SQLiteDataReader reader = sqCommand.ExecuteReader();
if(reader.Read())
return reader.GetString(0);
else
return ""; // or whatever you want to return if no records are present
}
} catch (Exception e) {
// do exception handling
}
Said that, remember that if you want to retrieve just one column from a single row like you have in your query then it is better to use ExecuteScalar instead of ExecuteReader
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
object result = sqCommand.ExecuteScalar();
return result != null ? result.ToString() : "";
}
} catch (Exception e) {
// do exception handling
}
Related
I am validating existance of a record before it inserts new record in. However the code does not jump into insert block. it jumps to finally block after reader.close
i suspect i must be doing something wrong as there is no exceptions and the recordsaffected =-1 every time
{
// check if data exists before inserting
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = " SELECT id from [dbo].[trade_events] where instrument =#instrument and datetime>#datetime and eventname=#eventname";
command.Parameters.AddWithValue("#datetime", dt);
command.Parameters.AddWithValue("#instrument", instrument);
command.Parameters.AddWithValue("#eventname", eventname);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) ;
reader.Close();
if (reader.RecordsAffected <= 0)
{
// <== insert data now
command.CommandText = " INSERT INTO [dbo].[trade_events] ([datetime] ,[instrument],[source],[eventname],[eventdetails],[state],[account],[connection]) " +
"values(#datetime, #instrument,#source, #eventname,#eventdetails,#state,#account,#connection)";
// command.CommandText = "UPDATE [dbo].[Alerts] SET [datetimealertrecieved] = #timerecieved where instrument=#instrument and alertType =#alerttype and datetimealertrecieved is null" ;
command.Parameters.AddWithValue("#datetime", dt);
command.Parameters.AddWithValue("#instrument", instrument);
command.Parameters.AddWithValue("#source", source);
command.Parameters.AddWithValue("#eventname", eventname);
command.Parameters.AddWithValue("#eventdetails", eventdetails);
command.Parameters.AddWithValue("#state", state);
command.Parameters.AddWithValue("#account", account);
command.Parameters.AddWithValue("#connection", dataconnection);
connection.Open();
int recordsAffected = command.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
// error here
var notice = airbrake.BuildNotice(ex);
var response = airbrake.NotifyAsync(notice).Result;
notice = airbrake.BuildNotice(command.CommandText);
response = airbrake.NotifyAsync(notice).Result;
}
finally
{
connection.Close();
}
reader.RecordsAffected isn't reliable, and isn't intended for this, ultimately; if you're trying to test for existence, well... the "least changes to the code" way would be to increment a counter to count the rows:
int records = 0;
while (reader.Read()) records++;
if (records == 0) {...}
or perhaps the more lazy:
bool any = reader.Read();
if (any) {...}
but: that's unnecessarily noisy; you could select count(1) from..., but even that is an extra round trip. An OK-ish idea might be the single SQL operation (that does the existence and insert in the same batch):
if not exists (
select id from [dbo].[trade_events]
where instrument =#instrument and datetime>#datetime and eventname=#eventname
)
begin
insert into ...
end
but even this is still a race condition, unless you have suitable transactional isolation.
If you look at the documentation of RecordsAffected it's clear why you get -1:
The number of rows changed, inserted, or deleted; 0 if no rows were
affected or the statement failed; and -1 for SELECT statements.
You can use the HasRows property:
using(SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
// insert
}
}
You have to clear previous parameters before adding the new ones:
var cont=false;
while (reader.Read())
{
....your code
cont=true ;
}
reader.Close();
if (cont)
{
command.Parameters.Clear();
command.Parameters.AddWithValue("#datetime", dt);
command.Parameters.AddWithValue("#instrument", instrument);
command.Parameters.AddWithValue("#source", source);
command.Parameters.AddWithValue("#eventname", eventname);
.....
}
Is it possible to use SQL command for login and update at the same time? I mean when the Login is done, I want to change logat in 1. Do I have to create a new if with OpenConnection()?
public bool IsLogin(string user, string pass) {
string query = $"SELECT * from utiliz WHERE username='{user}' AND password='{GetSha1(pass)}'";
string query_update = $"UPDATE utiliz SET logat='{1}' WHERE username='{user}'";
try
{
if (OpenConnection())
{
MySqlCommand cmd = new MySqlCommand(query, conn);
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
reader.Close();
conn.Close();
return true;
}
else
{
reader.Close();
conn.Close();
return false;
}
}
else {
conn.Close();
return false;
}
}
catch (Exception ex) {
conn.Close();
return false;
}
}
EDITED
Guys, I have edited my code, following parameters procedure. Is it good what I did?
if (String.IsNullOrEmpty(textBox_pass_login.Text) && String.IsNullOrEmpty(textBox_usr_login.Text) || String.IsNullOrEmpty(textBox_usr_login.Text) || String.IsNullOrEmpty(textBox_pass_login.Text))
{
System.Windows.Forms.MessageBox.Show("Both fields (username,password) are required");
}
else
{
string user = textBox_usr_login.Text;
string password = textBox_pass_login.Text;
string encryptedpass = GetSha1(password);
try
{
using (var connection = conn)
{
string query = "SELECT * from utiliz WHERE username=#user AND password=#password";
using (var command = new MySqlCommand(query, conn))
{
command.Parameters.AddWithValue("#user", user);
command.Parameters.AddWithValue("#password", encryptedpass);
MySqlDataAdapter sda = new MySqlDataAdapter(command);
DataTable dt = new DataTable();
sda.Fill(dt);
conn.Open();
int i = command.ExecuteNonQuery();
conn.Close();
if (dt.Rows.Count > 0)
{
this.Hide();
var form2 = new Form1();
form2.Closed += (s, args) => this.Close();
form2.Show();
}
else
{
System.Windows.Forms.MessageBox.Show("Wrong credentials");
textBox_usr_login.Clear();
textBox_pass_login.Clear();
}
}
}
}
catch
{
System.Windows.Forms.MessageBox.Show("Wrong credentials");
textBox_usr_login.Clear();
textBox_pass_login.Clear();
}
}
There are some important points here. You must use Parameterized queries to improve the performence of your query on the database layer and avoid some problems such as sql injection. You also could use transactions to keep data integrity.
Check the sample bellow with comments (I didn't test this code, may not work properly on your environment):
public bool IsLogin(string user, string pass)
{
// prepare the queries with parameters with '#' and parameter name
const string query = "SELECT count(username) from utiliz WHERE username = #username AND password = #password";
const string query_update = "UPDATE utiliz SET logat = #logat WHERE username = #username";
// prepare the encrypted password
string encryptedPass = GetSha1(pass);
// use a result variable to use as the function result
bool result = false;
try
{
if (OpenConnection())
{
// start a transaction from the connection object
using (MySqlTransaction tran = conn.BeginTransaction())
{
try
{
int userFound = 0;
// prepare the MySqlCommand to use the query, connection and transaction.
using (MySqlCommand userCommand = new MySqlCommand(query, conn, tran))
{
userCommand.Parameters.AddWithValue("#username", user);
userCommand.Parameters.AddWithValue("#password", encryptedPass);
userFound = (int) userCommand.ExecuteScalar();
}
if (userFound > 0)
{
// prepare the MySqlCommand to use the query, connection and transaction to update data
using (MySqlCommand logatCommand = new MySqlCommand(query_update, conn, tran))
{
logatCommand.Parameters.AddWithValue("#logat", DateTime.Now);
logatCommand.Parameters.AddWithValue("#username", user);
logatCommand.ExecuteNonQuery();
}
}
// commit the transaction
tran.Commit();
result = true;
}
catch (Exception ex)
{
// perform some log with ex object.
tran.Rollback();
}
finally
{
conn.Close();
}
}
}
}
catch (Exception e)
{
// perform some log...
return false;
}
return result;
}
As recommended (and demonstrated) by Felipe Oriani, you should use parameterized queries.
Let me pinpoint, however, that you can do this with a single update query. The trick is to filter the update query on both user name and password:
UPDATE utiliz SET logat = #logat WHERE username = #username AND password = #password
You want to run the query with method ExecuteNonQuery, which returns the number of rows affected.
If credentials are valid, the where cause selects the relevant record and the update happens, returning 1 as the count of records affected. Else, no record is updated, and the method returns 0.
I am trying to retrieve data from a MYSQL table that has 2 rows but, only the first row is returned. The SQL statement used is very simple sqlQuery = "SELECT * FROM table"
With the code below the class only returns the first value found
private ArrayList dbRead(String sqlQuery, String classQuery)
{
ArrayList dbCategoryResults = new ArrayList();
// *** CONNECT TO DATABASE
Console.WriteLine("** Database Connection: Connecting to database");
MySqlConnection dbConnection = new MySqlConnection(dbStringConnection);
try
{
dbConnection.Open();
Console.WriteLine("** Database Connection: Connected to database server");
// *** READ FROM DATABASE
MySqlCommand command = new MySqlCommand(sqlQuery, dbConnection);
MySqlDataReader dataReader = command.ExecuteReader();
if (dataReader.Read())
{
if (classQuery == "categories")
{
//String det = dataReader[1].ToString();
dbCategoryResults.Add(dataReader[1]);
Console.WriteLine("Found " + dbCategoryResults.Count);
return dbCategoryResults;
}
}
dataReader.Close();
command.Dispose();
dbConnection.Close();
}
catch (MySqlException e)
{
Console.WriteLine(e.ToString());
MessageBox.Show(e.Message, "Database Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// CLOSE DATABASE
try
{
dbConnection.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
return null;
}
Its as simple as
// Always call Read before accessing data.
// Advances the MySqlDataReader to the next record.
// returns true if it finds a record
while (dataReader.Read())
{
// depending on your query
Console.WriteLine(myReader.GetInt32(0) + ", " + myReader.GetString(1));
}
if you want the number of rows you could always make use of a DataTable or even just use a count in the while loop. DataTable Example :
DataTable dt = new DataTable();
dt.Load(reader);
Console.WriteLine("Rows returned : " + dt.Rows.Count);
foreach(DataRow dr in dt.Rows)
{
Console.WriteLine(dr["SomeResultingColumn"]);
}
I would like to find a way to exit out of datareader after the if statement so that I can execute the insert query in else statement. Is there a way to do it?
I am getting the error that dr is still open and hence cannot perform the below query.
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
Here is the code:
public class VendorDetails
{
int _VendorID;
string _VendorName;
public int VendorID
{
set { _VendorID = value; }
get { return _VendorID; }
}
public string VendorName
{
set { _VendorName = value; }
get { return _VendorName; }
}
}
public VendorDetails VendorCheck(string sVendorName)
{
SqlCommand cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = sVendorName;
VendorDetails sVendorDetails = null;
try
{
myConnection.Open();
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
else if (dr.HasRows!= true)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
dr.Close();
return sVendorDetails;
}
catch (SqlException err)
{
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message);
}
finally
{
myConnection.Close();
}
}
You will need to close/dispose of your DataReader prior to reusing the connection, as it's still being used.
Maybe something like this?
var readerHasRows = false;
using (var dr = cmd.ExecuteReader())
{
readerHasRows = dr.HasRows;
if(readerHasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
}
if(!readerHasRows)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
VendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
There are a few things I would like to mention
Your main issue is that you are not closing your DataReader. You can use the using statement for it
You don't need to explicitly open and close the SqlConnection. The SqlCommand object will do it as needed.
You don't need to check with if (dr.HasRows) and then check again in while (dr.Read()). Also, you don't need to loop to pick up only one row of data.
Ideally, I would put the "Fetch" part in a separate function and the "insert" in a separate function, so the functions stay small and reusable.
Your pattern is superfluous if (flag) {TakeAction();} else if (!flag) {TakeAction2();}. Every time the code hits theelse, it will also hit theif (!flag)`
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery(); line looks fishy. If your Stored Procedure returns the VendorId, then you should use ExecuteScalar. Currently it is just storing 1 in all case since you are presumably inserting one row.
Don't discard the original SqlException when creating a custom ApplicationException. Upstream system might want to know more details than you are passing. Pass it along as the InnerException
I have also changed some stylistic aspects:
The variable names changed to the more commonly used camelCase, instead of the incorrectly used Hungarian Notation (sVendorDetails instead of oVendorDetails)
Brace in K&R style
Used var when the right side is a new statement
Use Object Initializers instead of creation+assignment
Below is the code
public VendorDetails VendorCheck(string vendorName, SqlConnection myConnection) {
try {
return GetVendor(vendorName, myConnection) ?? InsertVendor(vendorName, myConnection);
} catch (SqlException err) {
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message, err);
}
}
VendorDetails GetVendor(string vendorName, SqlConnection myConnection) {
using (var cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = vendorName;
using (SqlDataReader dr = cmd.ExecuteReader()) {
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + vendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true); // TODO: Does this really belong here!?!?
if (dr.Read()) {
return new VendorDetails {
VendorID = ((int)dr["VendorID"]),
VendorName = ((string)dr["VendorName"]).ToUpper().Trim()
};
}
}
}
return null;
}
VendorDetails InsertVendor(string vendorName, SqlConnection myConnection) {
using (var insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection)) {
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = vendorName;
return new VendorDetails {
VendorID = (int)insertcmd.ExecuteScalar(),
VendorName = vendorName
};
}
}
I have a problem with my SQL datareader, i want to make an external class and make the code in my xaml.cs as short as possible, because there are a lot of sqldatareaders needed in my program. for this I want to pass following two strings to the datareader class:
public void refreshcombobox()
{
cbGebruiker.Items.Clear();
database = new DataBase();
string sqlrdr = "(rdr.GetString(1).ToString().Trim())";
List<string> reader = database.ReaderRdr("Select * from Gebruikers", ref sqlrdr);
foreach (String str in reader)
{
cbGebruiker.Items.Add(str);
}
}
however, when I do this this is the result in my program instead of the actual results that are stored in the database:
http://i58.tinypic.com/301j2vo.jpg (I can't post images)
can somebody help me with this? I've searched everywhere...
I don't know how to pass the rdr.GetString(1).ToString().Trim() to make it actually look stuff up in the db. Instead of just copying the string directly into the list.
This is the class:
namespace ClassLib
{
public class DataBase
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["kassadatabase"].ConnectionString);
public object ScalarObject(string sql)
{
object value = null;
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
value = cmd.ExecuteScalar();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (conn != null) conn.Close();
}
return value;
}
public List<string> ReaderRdr(string sql)
{
SqlDataReader rdr = null;
List<string> reader = new List<string>();
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
//reader.Add(rdr.GetString(1).ToString().Trim());
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (rdr != null) rdr.Close();
if (conn != null) conn.Close();
}
return reader;
}
//public List<string> ReaderRdr(string sql, ref string str)
//{
// SqlDataReader rdr = null;
// List<string> reader = new List<string>();
// try
// {
// conn.Open();
// SqlCommand cmd = new SqlCommand(sql, conn);
// rdr = cmd.ExecuteReader();
// while (rdr.Read())
// {
// //MessageBox.Show(str.ToString());
// //var strRdr = str;
// //MessageBox.Show(strRdr.ToString());
// //reader.Add(rdr.GetString(1).ToString().Trim());
// reader.Add(str);
// Console.WriteLine(String.Format("{0}", rdr[0]));
// }
// }
// catch (SqlException ex)
// {
// MessageBox.Show(ex.Message);
// }
// finally
// {
// if (rdr != null) rdr.Close();
// if (conn != null) conn.Close();
// }
// return reader;
//}
public void ExecuteNQuery(string insertString)
{
try
{
conn.Open();
SqlCommand cmd2 = new SqlCommand(insertString, conn);
cmd2.ExecuteNonQuery();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (conn != null) conn.Close();
}
}
}
}
public List<string> ReaderRdr(string sql)
{
SqlDataReader rdr = null;
List<string> reader = new List<string>();
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
//reader.Add(rdr.GetString(1).ToString().Trim());
}
.....
now in the methode public list i want to replace the //reader.Add(rdr.GetString(1).ToString().Trim()); part(wich works fine)
with a string that is passed to the method.
public List<string> ReaderRdr(string sql, string strRdr)
{
SqlDataReader rdr = null;
List<string> reader = new List<string>();
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
//reader.Add(strRdr);
}
I'm not 100% sure what you're trying to do, but I can tell you right now that this is what you're doing wrong - the string (str) you're passing to ReaderRdr is just a string literal of C# code. There's super hacky (super inadvisable) things you can do to mimic what exists in other languages as eval(), but there's no built-in way to do that in C#. Nothing (sensible) you do to "(rdr.GetString(1).ToString().Trim())" is ever going to get a string, or cast it to string, or trim anything.
Within your ReaderRdr function, all you're accomplishing is just to add the string str to your List<string> reader. This accomplishes nothing and has no bearing whatsoever on the results you get from your database query in your SqlDataReader rdr. If you want to store the data you actually get from your database, use rdr, not the (useless) string argument str.
Also, I feel like you must have left something out of your code - you're instantiating your SqlCommand cmd with conn as your second argument, but I don't see that defined anywhere within your ReaderRdr method, and it's not an argument passed to ReaderRdr. You don't have an SqlConnection object as a field or property within your class, do you?
As far as what you should maybe do, despite lacking much of any context in terms of your actual aims - if you want to get any given column of the result for each row returned by your SqlDataReader:
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
var yourDataCell = rdr[yourColumnIndex];
// or:
var yourDataCellOtherWay = rdr["YourColumnName"];
}
Alternately, you can just iterate through each of the cells in any given row produced by your SqlDataReader like so:
for(int i = 0 ; i < numberOfColumns; i++) {
// do something with rdr[i] here
}
I'm not sure if there's anything you can do establish numberOfColumns based on the state of your SqlDataReader, but others might know better.
You actually adding the String that you passed to a function to your reader reader.Add(str); You get the response from SQL I your rdr.
This item will show you something from your database:
Console.WriteLine(String.Format("{0}", rdr[0]));