The second query does not run - c#

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);
.....
}

Related

object reference not set to an instance of an object >> insert query c#

I have this method which inserts data to two different tables in my database from textboxes and Datagridview , The first insert query executing normally but the second one throws an error
Object reference not set to an instance of an object
Here is my method:
private void InsertAll()
{
DialogResult Result = MessageBox.Show("Do you want to save all ? ", "Warnning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (Result == DialogResult.OK)
{
using (SqlCommand cmd = new SqlCommand())
{
try
{
cmd.Connection = Cn;
cmd.CommandType = CommandType.Text;
Cn.Open();
// Insert the header first Due to one-to-many relationship
cmd.CommandText = #" INSERT INTO DocDtls ( DocNum, zDate, Warehouse, Orientation,TransType,UserName )
VALUES (#prm1, #prm2, #prm3, #prm4, #prm5, #prm6);";
cmd.Parameters.AddWithValue("#prm1", txtDocNum.Text);
cmd.Parameters.AddWithValue("#prm2", txtDate.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#prm3", cmbWh.Text);
cmd.Parameters.AddWithValue("#prm4", txtOrient.Text);
cmd.Parameters.AddWithValue("#prm5", txtTransType.Text);
cmd.Parameters.AddWithValue("#prm6", txtUser.Text);
cmd.ExecuteNonQuery();
// Insert the Details >> where the problem start to occur
if (txtTransType.Text == "Release")
{
cmd2.Connection = Cn;
cmd2.CommandType = CommandType.Text;
// Cn.Open();
for (int i = 0; i < DGV1.Rows.Count; i++)
{
cmd2.CommandText = #"INSERT INTO Transactions ( DocNum, Code, QtyIn, QtyOut, BalanceAfter, Remarks, Unit)
VALUES (#prm1, #prm2, #prm3, #prm4, #prm5, #prm6,#prm7);";
cmd2.Parameters.AddWithValue("#prm1", txtDocNum.Text);
cmd2.Parameters.AddWithValue("#prm2", DGV1.Rows[i].Cells["Code"].Value);
cmd2.Parameters.AddWithValue("#prm3", 0);
cmd2.Parameters.AddWithValue("#prm4", DGV1.Rows[i].Cells["Qty"].Value);
cmd2.Parameters.AddWithValue("#prm5", DGV1.Rows[i].Cells["BalanceAfter"].Value);
cmd2.Parameters.AddWithValue("#prm6", DGV1.Rows[i].Cells["Remarks"].Value);
cmd2.Parameters.AddWithValue("#prm7", DGV1.Rows[i].Cells["Unit"].Value);
cmd2.ExecuteNonQuery();
MessageBox.Show("Registered", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
else
{
cmd2.Connection = Cn;
cmd2.CommandType = CommandType.Text;
//Cn.Open();
for (int i = 0; i < DGV1.Rows.Count; i++)
{
cmd2.CommandText = #"INSERT INTO Transactions ( DocNum, Code, QtyIn, QtyOut, BalanceAfter, Remarks, Unit )
VALUES (#prm1, #prm2, #prm3, #prm4, #prm5, #prm6,#prm7);";
cmd2.Parameters.AddWithValue("#prm1", txtDocNum.Text);
cmd2.Parameters.AddWithValue("#prm2", DGV1.Rows[i].Cells["Code"].Value);
cmd2.Parameters.AddWithValue("#prm3", DGV1.Rows[i].Cells["Qty"].Value);
cmd2.Parameters.AddWithValue("#prm4", 0);
cmd2.Parameters.AddWithValue("#prm5", DGV1.Rows[i].Cells["BalanceAfter"].Value);
cmd2.Parameters.AddWithValue("#prm6", DGV1.Rows[i].Cells["Remarks"].Value);
cmd2.Parameters.AddWithValue("#prm7", DGV1.Rows[i].Cells["Unit"].Value);
cmd2.ExecuteNonQuery();
MessageBox.Show("Registered", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message.ToString(), "Error Message",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
Cn.Close();
}
}
}
I suspected that the connection might be closed in some point so I added opening connection again after the first insert but the problem still happening.
What's wrong with my code?
Thanks in advance
Try this. There's a lot of code, so likely a typo or other issue or two still here. Pay attention to the comments:
private void InsertAll()
{
using (var cn = new SqlConnection(Cn.ConnectionString)) // Using block around the connection
using (var cmd = new SqlCommand("", cn))
{
//removed the try/catch. Instead, wrap the try/catch around this method call
cmd.CommandText = #" INSERT INTO DocDtls ( DocNum, zDate, Warehouse, Orientation,TransType,UserName )
Values (#prm1, #prm2, #prm3, #prm4, #prm5, #prm6);";
//AddWithValue() can be dangerously slow, but usually still safe for single simple INSERT
cmd.Parameters.AddWithValue("#prm1", txtDocNum.Text);
cmd.Parameters.AddWithValue("#prm2", txtDate.Value); //If you're converting a DateTime to a string for use in SQL, you're doing something VERY WRONG
cmd.Parameters.AddWithValue("#prm3", cmbWh.Text);
cmd.Parameters.AddWithValue("#prm4", txtOrient.Text);
cmd.Parameters.AddWithValue("#prm5", txtTransType.Text);
cmd.Parameters.AddWithValue("#prm6", txtUser.Text);
cn.Open(); //Wait until last possible moment to call cn.Open()
cmd.ExecuteNonQuery();
// Normally don't re-use the same connection, but
// within a single operation context like this it's okay
// not only for the connection, but for the command, too.
cmd.CommandText = #" INSERT INTO Transactions ( DocNum, Code, QtyIn, QtyOut, BalanceAfter, Remarks, Unit )
Values (#DocNum, #Code, #QtyIn, #QtyOut, #BalanceAfter, #Remarks,#Unit);";
cmd.Parameters.Clear(); //Missing this line is why you had to create cmd2 before.
// Use exact types from the database here. Don't let ADO.Net try to infer these.
cmd.Parameters.Add("#DocNum", SqlDbType.VarChar, 10).Value = txtDocNum.Text;
cmd.Parameters.Add("#Code", SqlDbType.Char, 7);
// the "= 0" at the end of the next two lines are important.
cmd.Parameters.Add("#QtyIn", SqlDbType.Int).Value = 0;
cmd.Parameters.Add("#QtyOut", SqlDbType.Int).Value = 0;
cmd.Parameters.Add("#BalanceAfter", SqlDbType.Decimal);
cmd.Parameters.Add("#Remarks", SqlDbType.NVarChar, 1000);
cmd.Parameters.Add("#Unit", SqlDbType.NVarChar, 10);
// We can also set the Qty switch outside the loop
var qtyKey = (txtTransType.Text == "Release")?"#QtyOut":"#QtyIn";
for (int i = 0; i < DGV1.Rows.Count; i++)
{
// Only change parameter values inside the loop
cmd.Parameters["#Code"] = DGV1.Rows[i].Cells["Code"].Value;
cmd.Parameters[qtyKey].Value = DGV1.Rows[i].Cells["Qty"].Value;
cmd.Parameters["#BalanceAfter"] = DGV1.Rows[i].Cells["BalanceAfter"].Value;
cmd.Parameters["#Remarks"] = DGV1.Rows[i].Cells["Remarks"].Value;
cmd.Parameters["#Unit"] = DGV1.Rows[i].Cells["Unit"].Value;
cmd.ExecuteNonQuery();
}
// No need to call cn.Close(). Using blocks take care of it.
}
}
Note I removed the initial prompt/check, the try/catch, and the final complete message. Those things belong with the caller, like this:
DialogResult Result = MessageBox.Show("Do you want to save all ? ", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (Result == DialogResult.OK)
{
try
{
InsertAll()
MessageBox.Show("Registered", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch(Exception e)
{
MessageBox.Show(e.Message.ToString(), "Error Message",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
Eventually you would also move the method (and other database access) be a static member of a separate class, where the required information is passed as arguments.

How to access the first column in SQL and why does this code give me error?

Can someone tell my why my expectedNumber reader throws an error
The name reader does not exist in its current context
As far as I can see all this is doing is reading the first row and first column, don't understand why the reader is throwing a tantrum.
It doesn't like the line:
ExpectedNumber = reader.GetInt16(0);
The query is :
SELECT TOP (1) [ExpectedNumber]
FROM [dbo].[MyDatabase]
WHERE id = '{0}'
Code:
try
{
using (SqlCommand cmd = new SqlCommand(string.Format(Query, id), Connection))
{
Connection.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
// Check is the reader has any rows at all before starting to read.
if (reader.HasRows)
{
int ExpectedNumber = 0;
// Read advances to the next row.
while (reader.Read() == true)
{
// To avoid unexpected bugs access columns by name.
ExpectedNumber = reader.GetInt16(0);
}
Connection.Close();
return ExpectedResult;
}
Assert.Fail("No results returned from expected result query");
return 0;
}
}
}
catch (Exception e)
{
Connection.Close();
throw;
}
You should escape your query parameters, otherwise your code is vulnerable to SQL injection attacks, also, by using command parameters as in the example below you can make sure you are using the right data type (it seems you are trying to pass an int id as a string).
You are just trying to get one value so you don't need to use a reader and can use ExecuteScalar instead.
Finally, you don't need to handle closing the connection if you enclose it in a using block so you can avoid the try catch block as well.
string query = "SELECT TOP (1) [ExpectedNumber] FROM [dbo].[MyDatabase] WHERE id = #id";
using (var connection = new SqlConnection("connStr"))
{
connection.Open();
using (var cmd = new SqlCommand(query, connection))
{
cmd.Parameters.Add("#id", SqlDbType.Int).Value = id;
object result = cmd.ExecuteScalar();
if (result != null && result.GetType() != typeof(DBNull))
{
return (int)result;
}
Assert.Fail("No Results Returned from Expected Result Query");
return 0;
}
}
Note: this code assumes you are using SQL Server, for other systems the format of the parameters in the connection string might change, e.g. for Oracle it should be :id instead of #id.

How to return data using sqCommand.ExecuteReader();?

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
}

How to get all my database table values in a listbox with the for statement C#

As a beginner in C# I am having some problems with getting all my values (and only the values - not the field names) out of my database table and list them in my listbox.
I did some research and found out that I could get all the field names of a specific table with the following code:
try
{
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
string query = "select * from MyTable where Month='January'";
command.CommandText = query;
OleDbDataReader reader = command.ExecuteReader();
var columns = listBox5;
for (int i = 0; i < reader.FieldCount; i++)
{
if (reader.GetName(i) != "Month" && reader.GetName(i) != "Id")
{
columns.Items.Add(reader.GetName(i));
}
}
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error" + ex);
}
This works perfectly for listing the field names (and not the field names Month and ID). Though now I also need to list all my values (that match with the corresponding field names). At first I thought to add them at the same time with the field names (above code) but this was to complicated for me. So then I try'd to list the values in another listbox with kinda the same code, but instead of using the GetName I used the GetValue. See code below:
try
{
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
string query = "select * from MyTable";
command.CommandText = query;
OleDbDataReader reader = command.ExecuteReader();
var columns2 = listBox6;
for (int i = 0; i < reader.FieldCount; i++)
{
columns2.Items.Add(reader.GetValue(i).ToString());
}
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error" + ex);
}
This doesn't work though. I am getting the following error:
ErrorSystem.InvalidOperationException: Data doesn't exist for the row/column at line ...
On this line:
columns2.Items.Add(reader.GetValue(i).ToString());
You need to call Read() on your reader before you can read records:
var columns2 = listBox6;
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
columns2.Items.Add(reader.GetValue(i).ToString());
}
}
From MSDN:
Advances the SqlDataReader to the next record. The default position of the SqlDataReader is before the first record. Therefore, you must call Read to begin accessing any data.

Checking the number of rows returned from MySQL Data Reader

I am currently working on an C# project and I am trying to get the number of rows returned from MySQL Data Reader.
I know there is no direct function so I am trying to write my own. In the function, I pass it the MySQLDataReader object and then loop through the MySQL Data Reader and increment a counter and return the value of the counter.
This then seems to lock up the program, I guess because I am Reader.read() after I've got the count I'm already at the end. Instead I have tried creating a copy of the reader and then loop through the temp version but I get the same result.
Below is my code where I am executing the query and calling the function.
string query = "SELECT * FROM reports, software, platforms, versions "
+ "WHERE EmailVerified = #verified AND reports.SoftwareID = software.id AND reports.PlatformID = platforms.id "
+ "AND reports.VersionID = versions.id AND BugReportAcceptedNotificationSent = #notificationSent";
using (MySqlCommand cmd = new MySqlCommand(query, db.conn))
{
cmd.Parameters.AddWithValue("#verified", "1");
cmd.Parameters.AddWithValue("#notificationSent", "0");
using (MySqlDataReader reader = cmd.ExecuteReader())
{
totalEmails = HelperClass.totalRowsInMySQLDataReader(reader);
while (reader.Read())
{
currentEmailCount++;
EmailNotifications emailNotification = new EmailNotifications(reader);
emailNotification.sendNewBugReportAfterVerificationEmail(currentEmailCount, totalEmails);
}
}
}
Below is my function that gets the row count
public static int totalRowsInMySQLDataReader(MySqlDataReader reader)
{
MySqlDataReader tempReader = reader;
ILibraryInterface library = GeneralTasks.returnBitsLibrary(Configuration.databaseSettings, Configuration.engineConfig.logFile);
string methodInfo = classDetails + MethodInfo.GetCurrentMethod().Name;
try
{
int count = 0;
while (tempReader.Read())
{
count++;
}
tempReader = null;
return count;
}
catch (Exception ex)
{
string error = string.Format("Failed to get total rows in MySQL Database. Exception: {0}", ex.Message);
library.logging(methodInfo, error);
library.setAlarm(error, CommonTasks.AlarmStatus.Medium, methodInfo);
return -1;
}
}
Make use of a DataTable to load your results, e.g.
DataTable dt = new DataTable();
dt.Load(reader);
int numberOfResults = dt.Rows.Count;
You can then also iterate over the rows to read the values, e.g.
foreach(DataRow dr in dt.Rows)
{
var value = dr["SomeResultingColumn"]
}
The other option is to issue two separate SQL statements, however you would need to ensure both statements were enclosed within a transaction with a Serializable isolation level, this is needed to make sure records aren't inserted between the execution of the two SQL statements.
To avoid multiple queries, how about including the total in the select itself?
SELECT COUNT(*) AS TotalNORows, * FROM reports, software, platforms, versions etc
i think without executing another command it's not possible...as there is no method available for count in reader class
you can try this... if it works..
string query = "SELECT * FROM reports, software, platforms, versions "
+ "WHERE EmailVerified=#verified AND reports.SoftwareID=software.id AND reports.PlatformID=platforms.id "
+ "AND reports.VersionID=versions.id AND BugReportAcceptedNotificationSent=#notificationSent";
using (MySqlCommand cmd = new MySqlCommand(query, db.conn))
{
cmd.Parameters.AddWithValue("#verified", "1");
cmd.Parameters.AddWithValue("#notificationSent", "0");
using (MySqlDataReader reader = cmd.ExecuteReader())
{
// create a new connection db.conn2 then
MySqlCommand cmd2 = new MySqlCommand(query, db.conn2))
cmd2.Parameters.AddWithValue("#verified", "1");
cmd2.Parameters.AddWithValue("#notificationSent", "0");
MySqlDataReader reader2 = cmd2.ExecuteReader();
int numberofrow=0;
while(reader2.Read())
numberofrow++;
//your codes......
}
Was working on the same problem. I hate having to iterate if a method is already available, but this is was the shortest bit I could come up with:
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader reader = cmd.ExecuteReader();
int rowcount = 0;
while(reader.Read()){
rowcount++;
}
First, create this class:
public static class Extensions
{
public static int Count(this MySqlDataReader dr)
{
int count = 0;
while(dr.Read())
count++;
return count;
}
}
This will implement .Count () on MySqlDataReader.
int count = reader.Count();
Exemple:
string sql= "SELECT * FROM TABLE";
MySqlCommand cmd = new MySqlCommand(sql, connection);
MySqlDataReader reader = cmd.ExecuteReader();
int count = reader.Count();
Maybe you could look things the other way around
You could just do a select count(*) and get the row count
or
use a data adapter to fill a container (like a DataTable) and count the rows
Unfortunatelly solution from Jan Van #Herck will return one row only, so in case you are interested in getting all rows and their number in one select, this isn't what you need.
In that case I suggest uou to try this:
select * , (select count(*) from my_table) AS numRow from my_table;
or read this:
Getting total mysql results returned from query with a limit: FOUND_ROWS error
You can use follwoing SQL Query to get the total rows Count.
SELECT COUNT(*) FROM [MYTABLE]
from the Code you can use ExecuteScalar() method to get the total number of rows returned by QUERY.
Try This:
int GetRowsCount(MySqlCommand command)
{
int rowsCount=Convert.ToIn32(command.ExecuteScalar());
return rowsCount;
}
Use above function as below:
MySqlCommand command=new MySlCommand("Select count(*) from MyTable",connectionObj);
int totalRows = GetRowsCount(command)
OleDbDataReader dbreader = new OleDbDataReader();
int intcount = 0;
if (dbreader.HasRows == true)
{
if (dbreader.Read())
{
intcount = dbreader.RecordsAffected;
}
}
"dbreader.RecordsAffected" will give you the number rows changed,inserted or deleted by the last statement of SQL

Categories