Apologies in advance if I missed an answer to this somewhere but I wasn't quite finding it anywhere. So I'm building an application that scans PDF's of service orders our company gets, parses it, and inserts it into a SQL DB. The problem is at the end of this code. It successfully :
saves the original pdf in the proper folder
scans the pdf and parses it
inserts the correct data into the service order table
grabs PK of service order just created as we need that for the next batch of inserts
Here is where it gets hung up with a Exception thrown: 'System.Data.SqlClient.SqlException' in System.Data.dll
I foreach through all the instruments as there are multiples per Service Order, but it is erroring on this somewhere. to be clear I put a break point on the insert statement and all of the data is good and in the proper format ("string" int)
I feel like its in my connection maybe?
Anyways, thanks in advance for the help.
string filename = Path.GetFileName(FileUpload1.FileName);
FileUpload1.SaveAs(Server.MapPath("~/PDF/") + filename);
// Now we parse the PDF by creating a new ServiceOrder object and parsing from it.
ServiceOrder so = new ServiceOrder();
// Make sure we load the PDF from the correct path on the server
so.LoadPDF(Server.MapPath("~/PDF/") + filename);
String strConnString = "Data Source=127.0.0.0;Initial Catalog=SOMECATALOG;User ID=SOMEUSER;Password=SOMEPASSWORD";
// Insert Into Service Orders Table
string defaultdate = DateTime.Now.ToString("yyyy-MM-dd");
String strQuery = "insert into TServiceOrders (strServiceOrderNo, intStatusCodeID, strCustomerName, strCustomerNo, strCustomerAddress1, strCustomerAddress2, strCustomerAddress3, intRepID, strServiceDescription, strServiceRequestDate, strServiceOrderDate, strNotes) values ('"
+ so.ServiceOrderNumber.ToString() + "', 2, '"
+ so.CustomerContactName.ToString() + "', '"
+ so.CustomerNumber.ToString() + "', '"
+ so.CustomerContactAddress1.ToString() + "', '"
+ so.CustomerContactAddress2.ToString() + "', '"
+ so.CustomerContactAddress3.ToString() + "', 1, '', '"
+ defaultdate + "', '" + defaultdate + "', '')";
SqlConnection conn = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand(strQuery, conn);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
// Grabbing latest primary key od service order just added for next instrument inserts
int lastid = 999999;
String strPKquery = "select top 1 intServiceOrderID from TServiceOrders order by intServiceOrderID desc";
SqlDataReader rdr = null;
SqlConnection conn2 = new SqlConnection(strConnString);
SqlCommand cmd2 = new SqlCommand(strPKquery, conn2);
try
{
conn2.Open();
rdr = cmd2.ExecuteReader();
while (rdr.Read())
{
lastid = (int)rdr["intServiceOrderID"];
}
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (conn2 != null)
{
conn2.Close();
}
}
// Insert Into Service Instruments Tables
SqlConnection conn3 = new SqlConnection(strConnString);
conn3.Open();
foreach (ServiceInstrument sin in so.ServiceInstruments)
{
string sim = "";
sim = sin.ServiceInstrumentModel;
if (String.IsNullOrEmpty(sim))
{
sim = "";
}
else
{
sim = sin.ServiceInstrumentModel.ToString();
}
string sid = "";
sid = sin.ServiceInstrumentDescription;
if (String.IsNullOrEmpty(sid))
{
sid = "";
}
else
{
sid = sin.ServiceInstrumentDescription.ToString();
}
string sis = "";
sis = sin.ServiceInstrumentSerial;
if (String.IsNullOrEmpty(sis))
{
sis = "";
}
else
{
sis = sin.ServiceInstrumentSerial.ToString();
}
string sih = "";
sih = sin.ServiceInstrumentHandle;
if (String.IsNullOrEmpty(sih))
{
sih = "";
}
else
{
sih = sin.ServiceInstrumentHandle.ToString();
}
string sip = "";
sip = sin.ServiceInstrumentParentAsset;
if (String.IsNullOrEmpty(sip))
{
sip = "";
}
else
{
sip = sin.ServiceInstrumentParentAsset.ToString();
}
String strQuery3 = "insert into TServiceInstruments values ('" + sim.ToString() + "', '" + sid.ToString() + "', '" + sis.ToString() + "', '" + sih.ToString() + "', " + sip.ToString() + ", " + lastid + ")";
SqlCommand cmd3 = new SqlCommand(strQuery3, conn3);
cmd3.ExecuteNonQuery();
}
conn3.Close();
When writing insert statements you should always specify the column names. This will protect the code from changes in the order of the columns in the table schema.
You are not using parameters in your sql statements, this leaves your code vulnerable to Sql Injection.
You should use using statements around your SqlConnection instances to ensure they are closed even when an Exception occurs.
Your logic is very difficult to follow, split your code until methods with meaningful names instead of having 1 "God" method that does everything.
If you follow those guidelines the problem will most likely solve itself in your refactoring.
Update Code Fragment
Note that you should always specify the correct types for your columns and the length if applicable. Also pass the actual value and never the string value.
const String strQuery3 = "INSERT INTO TServiceInstruments (sim, sid, sis, sih, sip, lid) VALUES (#sim, #sid, #sis, #sih, #sip, #lid)";
using(var conection = new SqlConnection(strConnString))
using(SqlCommand command = new SqlCommand(strQuery3, connection))
{
command.Parameters.Add(new SqlParameter("#sim", SqlDbType.VarChar, 200){Value = sim});
command.Parameters.Add(new SqlParameter("#sid", SqlDbType.VarChar, 200){Value = sid});
command.Parameters.Add(new SqlParameter("#sis", SqlDbType.VarChar, 200){Value = sis});
command.Parameters.Add(new SqlParameter("#sih", SqlDbType.VarChar, 200){Value = sih});
command.Parameters.Add(new SqlParameter("#sip", SqlDbType.Int){Value = sip});
command.Parameters.Add(new SqlParameter("#lid", SqlDbType.Int){Value = lid});
connection.Open();
command.ExecuteNonQuery();
}
Final note: You really need to learn how to read Exceptions and this includes the Stack Trace which points directly to the line in the call stack where the Exception originated. If you can understand this then debugging becomes much easier.
Maybe this doesn't deserve to be an answer, but I'm trying to build some reputation, so here goes :).
I suspect that your error lies in the "insert into TServiceInstruments ..." statement. Namely, you are giving the table more (or less) columns. As a good practice, always specify the columns, like this:
insert into TServiceInstruments (column1, column2, column3)
values (1, 2, 3)
Related
Hello guys, i am trying to get all records from tblInvoiceItemsTemp table and save all the records inside the tblInvoiceItems table but not able to solve. Any help would be appreciated, thank you.
I have added following code on btnSave_Click() event.
string connetionString1 = "server=localhost;database=billingDB;uid=root;pwd=root;integrated security=true";
using (MySqlConnection cnn1 = new MySqlConnection(connetionString1))
{
cnn1.Open();
string load_temp_table_rec_qry = "SELECT * FROM tblInvoiceItemsTemp";
using (MySqlCommand sqlcmd = new MySqlCommand(load_temp_table_rec_qry, cnn1))
{
MySqlDataReader temp_reader = sqlcmd.ExecuteReader();
while (temp_reader.Read())
{
string insert_invoice_items_qry = "INSERT INTO tblInvoiceItems(invoiceID, particulars, qty, rate) VALUES('" + 12 + "', '" + temp_reader["particulars"] + "', '" + temp_reader["qty"] + "', '" + temp_reader["rate"] + "')";
using (MySqlCommand itemsCmd = new MySqlCommand(insert_invoice_items_qry, cnn1))
{
itemsCmd.ExecuteNonQuery();
}
}
}
cnn1.Close();
}
I am getting following error messages.
An unhandled exception of type 'MySql.Data.MySqlClient.MySqlException' occurred in MySql.Data.dll
Additional Information: There is already an open DataReader associated with this Connection which must be closed first.
The error message is pretty clear: while you have a DataReader open (you haven't called Close/Dispose), the Connection cannot be used for anything else. One way to do this is to open a second connection:
using (MySqlCommand sqlcmd = new MySqlCommand(load_temp_table_rec_qry, cnn1))
{
MySqlDataReader temp_reader = sqlcmd.ExecuteReader();
using (var secondConnection = new MySqlConnection(connetionString1))
{
secondConnection.Open();
while (temp_reader.Read())
{
string insert_invoice_items_qry = "INSERT INTO tblInvoiceItems(invoiceID, particulars, qty, rate) VALUES('" + 12 + "', '" + temp_reader["particulars"] + "', '" + temp_reader["qty"] + "', '" + temp_reader["rate"] + "')";
using (MySqlCommand itemsCmd = new MySqlCommand(insert_invoice_items_qry, secondConnection))
{
itemsCmd.ExecuteNonQuery();
}
}
}
}
Another way is to use the disconnected model and load the records to a DataTable using a MySqlDataAdapter, so that the connection is free for using for itemsCmd.
However, you don't need to download into memory all the records for this, you can do an INSERT INTO SELECT, for much better performance:
INSERT INTO tblInvoiceItems(invoiceID, particulars, qty, rate)
SELECT 12, tblInvoiceItemsTemp.particulars, tblInvoiceItemsTemp.qty, tblInvoiceItemsTemp.rate
FROM tblInvoiceItemsTemp
I am getting this error while trying to select data from a database and I am lost on how to fix it. My query works in my database but I am not sure what is happening
randomget = "SELECT top 1 id, Department, Team, Process, SubProcess, SubTask, LastUpdatedBy from workload where stepstatus = 'complete' and agent is null or agent = 0 ";
OleDbConnection MyConn = new OleDbConnection();
MyConn.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=.......;Persist Security Info=False";;
MyConn.Open();
OleDbCommand cmd = MyConn.CreateCommand();
cmd.CommandText = randomget;
OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
int id = 0;
while (reader.Read())
{
id = int.Parse(reader[0].ToString());
textBox8.Text = reader[6].ToString();
textBox5.Text = reader[1].ToString();
textBox6.Text = reader[2].ToString();
textBox7.Text = reader[3].ToString();
textBox9.Text = reader[4].ToString();
textBox11.Text = reader[5].ToString();
break;
}
// input table name here
textBox2.Text = agentsrf.ToString();
string sqlupdate = "Update workload set agent = '" + agent + "', where ID = " + id + " ' ";
}
public OleDbConnection connection { get; set; }
You have one stray , after set agent = '" + agent + "' just remove it and then it works fine. Also you should use parameterized queries because this kind of string concatenations are open for SQL Injection:
string sqlupdate = "Update workload set agent = #agent where ID = #id ";
cmd.Parameters.AddWithValue("#agent", agent );
cmd.Parameters.AddWithValue("#id", id);
I solved it. One of my field names had a space in it, which I did not spot.
How Do I Find the ID from the first query and return this value so it can be inserted into query2? This is the code that needs done when a user completes a form on front end. I need to populate two tables and they will relate through the ID "StoryID" which is a primary key that is automatically created.
protected void Upload2_Click(object sender, EventArgs e)
{
userStoryForm.Visible = false;
info.Text = "You have successfully added a new user story.";
String connectionString = WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
String usernameData = username.Text.ToString();
int captureProjectID = Convert.ToInt32(Request.QueryString.Get("ProjectID"));
String storyno = StoryNoTextBox.Text;
String userstory = StoryTextTextBox.Text;
//Create connection
SqlConnection myConnection = new SqlConnection(connectionString);
//open connection
myConnection.Open();
String query = "INSERT INTO UserStories (StoryNo, StoryText, ProductOwner, ProjectID) " +
"VALUES ('" + storyno + "','" + userstory + "','" + usernameData + "','" + captureProjectID + "')" +
"SELECT SCOPE_IDENTITY() AS StoryID;";
SqlCommand myCommand = new SqlCommand(query, myConnection);
// Call GetOrdinal and assign value to variable.
SqlDataReader reader = myCommand.ExecuteReader();
int StoryIDData = reader.GetOrdinal("StoryID");
// Use variable with GetString inside of loop.
while (reader.Read())
{
Console.WriteLine("StoryID={0}", reader.GetString(StoryIDData));
}
// Call Close when done reading.
reader.Close();
//insert productowner, projectID and storyID into ProductBacklog table
String query2 = "INSERT INTO ProductBacklog (ProductOwner, ProjectID, StoryID) VALUES ('" + usernameData + "', #returnProjectID,'" + StoryIDData + "')";
SqlCommand myCommand2 = new SqlCommand(query2, myConnection);
myCommand2.Parameters.AddWithValue("#returnProjectID", captureProjectID);
//close connection
myConnection.Close();
}
}
Most important - use parameters in your SQL command. Never concatenate strings like that. You're asking for an SQL injection attack.
string query = #"
INSERT INTO UserStories (StoryNo, StoryText, ProductOwner, ProjectID)
VALUES (#storyno, #userstory, #usernameData, #captureProjectID)
SELECT CAST(SCOPE_IDENTITY() AS INT)";
SqlCommand myCommand = new SqlCommand(query);
myCommand.Parameters.Add("#storyno", DbType.String).Value = storyno;
...
To get the returned id, use ExecuteScalar():
int StoryIDData = (int)myCommand.ExecuteScalar();
Also, you don't dispose your resources correctly. If an exception is thrown in the method, the SQLConnection will not be closed. You should put it in a using statement.
I think I got the insert syntax but I always got this error. When I try different projects that are similar, it works just fine. Can you help me?
private void addbtn_Click(object sender, EventArgs e)
{
if (idkaryawantxt.Text != "")
{
string q = "insert into Table1 (Nama,No_Identitas,Alamat,Lahir,Tanggal_Lahir,Telepon,Divisi,Aktif,Password) values ('" + namakaryawantxt.Text.ToString() + "','" + identitastxt.Text.ToString() + "','" + alamattxt.Text.ToString() + "','" + lahirtxt.Text.ToString() + "','" + tgllahirtxt.Text.ToString() + "','" + tlpntxt.Text.ToString() + "','" + divisitxt.Text.ToString() + "','" + aktiftxt.Text.ToString() + "','" + passwordtxt.Text.ToString() + "')";
dosomething(q);
}
}
private void dosomething(String q)
{
try
{
connect.Open();
command.CommandText = q;
command.ExecuteNonQuery();
connect.Close();
loaddata();
}
catch (Exception e)
{
connect.Close();
MessageBox.Show(e.Message.ToString());
}
}
//REFRESH
private void loaddata()
{
datakaryawan.AllowUserToAddRows = false;
datakaryawan.Rows.Clear();
datakaryawan.Refresh();
connect.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\C# Project\minor\Karyawan.accdb;Persist Security Info=False;";
connect.Open();
command.Connection = connect;
command.CommandText = "SELECT * FROM Table1";
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
datakaryawan.Rows.Add();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["ID_Karyawan"].Value = reader[0].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Nama_Karyawan"].Value = reader[1].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["No_Identitas"].Value = reader[2].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Alamat"].Value = reader[3].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["PoB"].Value = reader[4].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["DoB"].Value = reader[5].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Telepon"].Value = reader[6].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Divisi"].Value = reader[7].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Aktif"].Value = reader[8].ToString();
datakaryawan.Rows[datakaryawan.Rows.Count - 1].Cells["Password"].Value = reader[9].ToString();
}
connect.Close();
idkaryawantxt.Text = datakaryawan.Rows[0].Cells[0].Value.ToString();
namakaryawantxt.Text = datakaryawan.Rows[0].Cells[1].Value.ToString();
identitastxt.Text = datakaryawan.Rows[0].Cells[2].Value.ToString();
alamattxt.Text = datakaryawan.Rows[0].Cells[3].Value.ToString();
lahirtxt.Text = datakaryawan.Rows[0].Cells[4].Value.ToString();
tgllahirtxt.Text = datakaryawan.Rows[0].Cells[5].Value.ToString();
tlpntxt.Text = datakaryawan.Rows[0].Cells[6].Value.ToString();
divisitxt.Text = datakaryawan.Rows[0].Cells[7].Value.ToString();
aktiftxt.Text = datakaryawan.Rows[0].Cells[8].Value.ToString();
passwordtxt.Text = datakaryawan.Rows[0].Cells[9].Value.ToString();
}
The word PASSWORD is reserved for MS-Access databases.
If you want to use it you need to encapsulate it in square brackets
string q = #"insert into Table1 (Nama,No_Identitas,Alamat,Lahir,Tanggal_Lahir,
Telepon,Divisi,Aktif,[Password]) values (.....)";
said that keep in mind that string concatenation to form a sql command is a bad practice and should be avoided at all costs using a parameterized query.
The worst problem with string concatenation to build sql commands is the possibility of Sql Injection attacks, but also strings that contain single quotes, dates and floating point values are a problem when you need to use their values to build a string concatenated query text.
For example, what happens if one of your text fields contains a single quote typed by your user?. Another syntax error because when you concatenate a string containing a quote you break the required syntax for the command.
So there is no other acceptable way than use a parameterized query
string q = #"insert into Table1 (Nama,No_Identitas,Alamat,Lahir,Tanggal_Lahir,
Telepon,Divisi,Aktif,[Password]) values (?,?,?,?,?,?,?,?,?,?)";
using(OleDbConnection connect = new OleDbConnection(.....))
using(OleDbCommand cmd = new OleDbCommand(q, connect)
{
connect.Open()
cmd.Parameters.AddWithValue("#p1", namakaryawantxt.Text);
... so on for the other 8 parameters
... REMEMBER TO ADD THEM IN THE SAME ORDER OF THE PLACEHOLDERS ...
cmd.ExecuteNonQuery();
}
// conn is read from handydrive
//conn2 read from C:\
this code is for write new record in to DB in C:\ by check exist first.
my problem is too slow for alot of records. and how to improve it to be faster...
SqlCeCommand cmd1 = new SqlCeCommand("Select * from bill_discount ", conn);
SqlCeDataReader dr1 = cmd1.ExecuteReader();
while (dr1.Read() != false)
{
SqlCeCommand cmd4 = new SqlCeCommand("Select * from bill_discount where bill_no='" + dr1.GetInt32(0) + "' AND bill_shopdc='" + dr1.GetString(2) + "' ", conn2);
SqlCeDataReader dr4 = cmd4.ExecuteReader();
if (dr4.Read() == false)
{
SqlCeCommand cmd2 = new SqlCeCommand("INSERT INTO bill_discount (bill_no,bill_value,bill_shopdc) VALUES ('" + dr1.GetInt32(0) + "','" + dr1.GetDouble(1) + "','" + dr1.GetString(2) + "') ", conn2);
// SqlCeDataReader dr2 = cmd2.ExecuteReader();
cmd2.ExecuteNonQuery();
}
}
//-------------------------------------------------------------------
I would take a look at the SqlBulkCopy Class:
Lets you efficiently bulk load a SQL
Server table with data from another
source.
BTW: In your code above, selecting the entire bill_discount table is not really a good idea, especially if the table is large.
[Also, it appears you could perform a single TSQL statement rather than looping through each row and round-tripping to the database.]
This example should be of help: SqlBulkCopy - Copy Table Data Between SQL Servers at High Speeds - ADO.NET 2.0 New Feature
Let's start by make the code more readable. Here's the result:
SqlCeCommand getAllBills = new SqlCeCommand("select * from bill_discount", primaryConnection);
SqlCeDataReader allBillsReader = getAllBills.ExecuteReader();
while (allBillsReader.Read())
{
SqlCeCommand getBill = new SqlCeCommand("select * from bill_discount where bill_no = '" + allBillsReader.GetInt32(0) + "' and bill_shopdc = '" + allBillsReader.GetString(2) + "' ", secondaryConnection);
SqlCeDataReader billReader = getBill.ExecuteReader();
if (!billReader.Read())
{
SqlCeCommand addMissingBill = new SqlCeCommand("insert into bill_discount (bill_no, bill_value, bill_shopdc) values ('" + allBillsReader.GetInt32(0) + "', '" + allBillsReader.GetDouble(1) + "', '" + allBillsReader.GetString(2) + "')", secondaryConnection);
addMissingBill.ExecuteNonQuery();
}
}
Disposable objects must be disposed. Let's do it.
Let's also remove SQL Injections.
Finally, let's optimize the second query: you don't need to select something and executing the reader if you just want to check if the value exists in the database.
using (SqlCeCommand getAllBills = new SqlCeCommand("select bill_no, bill_value, bill_shopdc from [bill_discount]", primaryConnection))
{
using (SqlCeDataReader allBillsReader = getAllBills.ExecuteReader())
{
while (allBillsReader.Read())
{
using (SqlCeCommand getBill = new SqlCeCommand("if exists(select * from [bill_discount] where [bill_no] = #billNumber and bill_shopdc = #billShop) select 1 else select 0", secondaryConnection))
{
getBill.Parameters.AddWithValue("#billNumber", allBillsReader["bill_no"]);
getBill.Parameters.AddWithValue("#billShop", allBillsReader["bill_shopdc"]);
bool billExists = Convert.ToBoolean(getBill.ExecuteScalar());
if (!billExists)
{
using (SqlCeCommand addMissingBill = new SqlCeCommand("insert into [bill_discount] ([bill_no], [bill_value], [bill_shopdc]) values (#billNumber, #billValue, #billShop)", secondaryConnection))
{
addMissingBill.Parameters.AddWithValue("#billNumber", allBillsReader["bill_no"]);
addMissingBill.Parameters.AddWithValue("#billValue", allBillsReader["bill_value"]);
addMissingBill.Parameters.AddWithValue("#billShop", allBillsReader["bill_shopdc"]);
int countAffectedRows = addMissingBill.ExecuteNonQuery();
Debug.Assert(countAffectedRows == 1, "The data was not inserted.");
}
}
}
}
}
}
So here we are.
Now, it's still a low performance solution. To be more effective, you might want to do the same thing in a single SQL query with joins. Since two tables are probably situated on different servers, you may look at linked servers: a feature that enables to execute a single query over several tables from several servers.
I see you are using SqlCe, which has number of limitations when inserting bulk data. The main limitation is the actual SqlCe Engine. You can however bypass this by using direct table inserts:
using (var command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandType = CommandType.TableDirect;
command.CommandText = TABLE_NAME_IN_SQL;
using (var rs = command.ExecuteResultSet(ResultSetOptions.Updatable))
{
var rec = rs.CreateRecord();
rec.SetInt32(0, value0); // the index represents the column numbering
rec.SetString(1, value1);
rec.SetInt32(2, value2);
rs.Insert(rec);
}
}