I have a script that is connecting to multiple databases and writing a query from each into a text file. However when I run it, I'm not getting the results expected. After some cross checking, it looks like it is writing the same results from the first database query instead of finding new results from the next connection. I inserted an IP string to verify the IPs are being grabbed by the for loop but it seems like I need some way of clearing the reader?
for (int z=0; z<2;z++) {
using (OleDbConnection connLocal = new OleDbConnection("Provider=SAOLEDB;LINKS=tcpip(host=" + ips[z] + ",PORT=2638);ServerName=EAGLESOFT;Integrated Security = True; User ID = dba; PWD = sql"))
try
{
connLocal.Open();
using (OleDbCommand cmdLocal = new OleDbCommand("SELECT tran_num, '" + ips[z] + "', provider_id, amount, tran_date, collections_go_to, impacts, type, '" + clinics[z] + "' AS Clinic FROM transactions WHERE tran_date LIKE '2015-11-23%'", connLocal))
using (StreamWriter sqlWriter = File.AppendText(#"C:\Users\Administrator\Desktop\Clinic.txt"))
{
using (OleDbDataReader readLocal = cmdLocal.ExecuteReader())
{
while (readLocal.Read())
{
sqlWriter.WriteLine("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}",
readLocal.GetValue(0).ToString(),
readLocal.GetValue(1).ToString(),
readLocal.GetValue(2).ToString(),
readLocal.GetValue(3).ToString(),
readLocal.GetValue(4).ToString(),
readLocal.GetValue(5).ToString(),
readLocal.GetValue(6).ToString(),
readLocal.GetValue(7).ToString(),
readLocal.GetValue(8).ToString());
}
readLocal.Close();
}
sqlWriter.Close();
connLocal.Close();
}
}
catch (Exception connerr) { Debug.WriteLine(connerr.Message); }
}
As always, any insight is much appreciated!
Related
I am trying to make an update on my MySql database, but I don't understand why it doesn't work when executing from MySqlCommand, I have the famous message "check the manual that corresponds to your MySQL server version" (I already have hundreds of queries working correctly, so I guess something is with syntax?).
edit 2 : Here is the part "near " :
'В-513',PRIORITY=1050,QUANTITY_INIT=28,QUANTITY_REMAINING=0,FICHIER='C:\\Actcut3' at line 1'
Here is the update query :
UPDATE launching_order_details SET
ID_LO=1935,
ID_CONTRACT=4228,
ID_PHASE=11765,
ID_ASS=235314,
LIST_REP_ORI='1005817//В-513//235314//В1007//11765//1//30',IS_SUBDETAIL=0,
REF_DETAIL='3201\\1\\В1007\\В-513\\',
NAME='В-513',
PRIORITY=1050,QUANTITY_INIT=28,QUANTITY_REMAINING=0,
FICHIER='C:\\Actcut3.10\\Data\\Parts\\3201\\1\\В1007\\В-513.ini' WHERE ID=27701
Of course I send it in a same line, I just splitted it here for better readability.
If I make a copy/paste of query, then execute it from phpMyadmin, all is working fine.
Edit : C# code :
DBConnect class :
public void Update(string query)
{
if (this.OpenConnection() == true)
{
if (isMySQL)
{
MySqlCommand cmd = new MySqlCommand(query.Replace("[vsteel].", ""), connection);
cmd.ExecuteNonQuery();
this.CloseConnection();
}
else
{
SqlCommand command = new SqlCommand(query, MSconnection);
command.Parameters.Add(new SqlParameter("0", 1));
//command.Connection = this.MSconnection;
command.ExecuteNonQuery();
this.CloseConnection();
}
}
}
public bool OpenConnection()
{
if (isMySQL)
{
try
{
connection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
System.Windows.Forms.Application.Exit();
Global.is_restarted = true;
return false;
}
}
else
{
try
{
MSconnection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
System.Windows.Forms.Application.Exit();
Global.is_restarted = true;
return false;
}
}
}
RepereLO class :
private void update()
{
this.listRepereOri = this.listRepereOri.OrderBy(x => x.Priority).ThenBy(x => x.ID).ToList();
DBConnect DataBase = new DBConnect();
string query = "UPDATE [vsteel].launching_order_details SET " +
"ID_LO=" + this.launchingOrder.ID + "," +
"ID_CONTRACT=" + this.contract.ID + "," +
"ID_PHASE=" + this.phase.ID + "," +
"ID_ASS=" + this.assembly.ID + "," +
"LIST_REP_ORI=\'" + convertListRepereOriToString() + "\'," +
"IS_SUBDETAIL=" + Convert.ToInt32(this.isSubRepere) + "," +
"REF_DETAIL=\'" + this.refDetail + "\'," +
"NAME=\'" + this.name + "\'," +
"PRIORITY=" + this.priority + "," +
"QUANTITY_INIT=" + this.quantity + "," +
"QUANTITY_REMAINING=" + this.remainingQuantity + "," +
"FICHIER=\'" + Global.ReplaceSpecialCharacters(this.fileName) + "\' " +
"WHERE ID=" + this.id;
DataBase.Update(query);
}
EDIT 2 : Parametirezed query
my DBConnect class
public void UpdateNew(string query, MySqlParameter[] myParamArray)
{
if (this.OpenConnection() == true)
{
using (MySqlCommand cmd = new MySqlCommand(query.Replace("[vsteel].", ""), connection))
{
for (int i = 0; i < myParamArray.Count(); i++)
{
cmd.Parameters.Add(myParamArray[i]);
}
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
}
In object :
private void update()
{
this.listRepereOri = this.listRepereOri.OrderBy(x => x.Priority).ThenBy(x => x.ID).ToList();
string query = "UPDATE [vsteel].launching_order_details SET " +
"ID_LO=#idLo," +
"ID_CONTRACT=#idContract," +
"ID_PHASE=#idPhase," +
"ID_ASS=#idAss," +
"LIST_REP_ORI=#listRepOri," +
"IS_SUBDETAIL=#isSubdetail," +
"REF_DETAIL=#refDetail," +
"NAME=#name," +
"PRIORITY=#priority," +
"QUANTITY_INIT=#qtyInit," +
"QUANTITY_REMAINING=#qtyRemaining," +
"FICHIER=#fichier" +
" WHERE ID=#id";
MySqlParameter[] listParams = new MySqlParameter[]
{
new MySqlParameter("id", this.id),
new MySqlParameter("idLo", this.launchingOrder.ID),
new MySqlParameter("idContract", this.Contract.ID),
new MySqlParameter("idPhase", this.Phase.ID),
new MySqlParameter("idAss", this.Assembly.ID),
new MySqlParameter("listRepOri", this.convertListRepereOriToString()),
new MySqlParameter("isSubdetail", this.isSubRepere),
new MySqlParameter("refDetail", this.refDetail),
new MySqlParameter("name", this.name),
new MySqlParameter("priority", this.priority),
new MySqlParameter("qtyInit", this.quantity),
new MySqlParameter("qtyRemaining", this.remainingQuantity),
new MySqlParameter("fichier", this.fileName),
};
DBConnect DataBase = new DBConnect();
DataBase.UpdateNew(query, listParams);
}
The actual problem is using string concatenation to construct a query from external input. This leaves the code wide open to SQL injection, conversion errors (what date format? decimal separator?) and ... syntax errors like this. What if Name is O'Reily for example? Or a user entered ' DROP TABLE Students; # ? No amount of escaping or replacing is going to fix the real bug - using string concatenation.
The correct way to do this is to use parameterized queries. This is actually easier than concatenating strings. If you use a library like Dapper, it's as easy as :
string sql=#"UPDATE [vsteel].launching_order_details
SET
ID_LO=#idlo,
ID_CONTRACT=#contract,
ID_PHASE=#phase,
ID_ASS=#assembly,
LIST_REP_ORI=#ori,
IS_SUBDETAIL=#isSubDetail,
REF_DETAIL=#ref,
NAME=#name,
PRIORITY=#priority,
QUANTITY_INIT=#initial,
QUANTITY_REMAINING=#remaining,
FICHIER=#path,
WHERE ID=#id";
using(var connection=new MySqlConnection(...))
{
connection.Execute(sql, new {
id,
idLo=launchingOrder.ID ,
contract=contract.ID,
....,
path=fileName});
}
Without Dapper, the code is a bit more complex but still easier and safer to write than string concatenation and trying to replace characters.
using(var connection=new MySqlConnection)
using (var cmd=new MySqlCommand(query,connection))
{
cmd.Parameters.AddWithValue("#id",this.id);
...
connection.Open();
cmd.ExecuteNonQuery();
}
BTW the DbConnect class has other issues as well. Long-lived database connections are a bug that harms performance and scalability. The locks taken during a connection remain active until it closes, which results in increased blocking for all clients. This happens even in databases with multi-version concurrency like PostgreSQL.
Connections are meant to be opened as late as possible and closed immediately after use. That's why you see all samples and tutorials create connections in a using block. This ensures the connection is close immediately after use.
ADO.NET uses connection pooling to eliminate the cost of opening a new connection, by reseting existing connections. When DbConnection.Close is called, the connection is reset and placed in a connection pool.
Tutorials
Basics of ADO.NET is a short intro to ADO.NET that explains what the various classes do and how they're used.
MySQL's Tutorial: An Introduction to Connector/NET Programming shows how to use ADO.NET with MySQL.
Microsoft's documentation on ADO.NET is almost an entire book that goes in great depth, so you should probably use it only as a reference
Dapper is a micro-ORM library that makes it very easy to map object properties to parameters and results to objects. It can be used with any ADO.NET provider, including MySQL.
With Dapper, one can write code like this :
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
public int IgnoredProperty { get { return 1; } }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });
And the library will map the Age and Id properties to #Age and #Id. It will also map the Age and Id columns in the results to Dog.Age and Dog.Id
I have a c# script task in an SSIS job that calls an API for the purpose of Geocoding. The API is proprietary and works something like this, receives request, takes address string, then attempts to string match to a huge list of addresses (millions) and if it cannot find it, then go out to another service such as google and get geodata info.
As you can imagine this string matching takes up a lot of time per request. Sometimes it's as slow as one request per min, and I have 4M addresses I need to do this for. Getting any dev work on the API side of things is not an option. To give a better picture of the process here is what I'm doing currently:
I pull a list of addresses from database (about 4M) and put it in a datatable and set variables:
// Fill c# datatable with query results
sdagetGeoData.Fill(dtGeoData);
// check to ensure datable has rows
if (dtGeoData.Rows.Count > 0)
{
// if datatable has rows, for every row set the varible
foreach (System.Data.DataRow row in dtGeoData.Rows)
{
localID = row[0].ToString();
address = row[1].ToString();
city = row[2].ToString();
state = row[3].ToString();
zip = row[4].ToString();
country = row[5].ToString();
// after varaibles are set, now run this method to post, get response and insert the string
GetGLFromAddress();
}
}
GetGLFromAddress() works like this:
Take the variables from above and form the JSON. Send the JSON using "POST" and httpWebRequest. Wait for request (time consuming). Return request. Set new variables with the return. Use those variables to update/ insert back into database, THEN loop through the next row in original datatable.
It's important to understand this flow because I need to be able to keep the localID variable with each request so I can update the correct record in the database.
Here is GetGLFromAddress():
private void GetGLFromAddress()
{
// Request JSON data with Payload
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http:");
httpWebRequest.Headers.Add("Authorization", "");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
// this takes the variables from your c# datatable and formats them for json post
var jS = new JavaScriptSerializer();
var newJson = jS.Serialize(new SeriesPost()
{
AddressLine1 = address,
City = city,
StateCode = state,
CountryCode = country,
PostalCode = zip,
CreateSiteIfNotFound = true
});
//// So you can see the JSON thats output
System.Diagnostics.Debug.WriteLine(newJson);
streamWriter.Write(newJson);
streamWriter.Flush();
streamWriter.Close();
}
try
{
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
// javascript serializer... deserializing the returned json so that way you can set the variables used for insert string
var p1 = new JavaScriptSerializer();
// after this line, obj is a fully deserialzed string of json Notice how I reference obj[x].fieldnames below. If you ever want to change the fiels or bring more in
// this is how you do it.
var obj = p1.Deserialize<List<RootObject>>(result);
// you must ensure the values returned are not null before trying to set the variable. You can see when that happens, I'm manually setting the variable value to null.
if (string.IsNullOrWhiteSpace(obj[0].MasterSiteId))
{
retGLMID = "null";
}
else
{
retGLMID = obj[0].MasterSiteId.ToString();
}
if (string.IsNullOrWhiteSpace(obj[0].PrecisionName))
{
retAcc = "null";
}
else
{
retAcc = obj[0].PrecisionName.ToString();
}
if (string.IsNullOrWhiteSpace(obj[0].PrimaryAddress.AddressLine1Combined))
{
retAddress = "null";
}
else
{
retAddress = obj[0].PrimaryAddress.AddressLine1Combined.ToString();
}
if (string.IsNullOrWhiteSpace(obj[0].Latitude))
{
retLat = "null";
}
else
{
retLat = obj[0].Latitude.ToString();
}
if (string.IsNullOrWhiteSpace(obj[0].Longitude))
{
retLong = "null";
}
else
{
retLong = obj[0].Longitude.ToString();
}
retNewRecord = obj[0].IsNewRecord.ToString();
// Build insert string... notice how I use the recently created variables
// string insertStr = retGLMID + ", '" + retAcc + "', '" + retAddress + "', '" + retLat + "', '" + retLong + "', '" + localID;
string insertStr = "insert into table " +
"(ID,GLM_ID,NEW_RECORD_IND,ACCURACY) " +
" VALUES " +
"('" + localID + "', '" + retGLMID + "', '" + retNewRecord + "', '" + retAcc + "')";
string connectionString = "Data Source=; Initial Catalog=; Trusted_Connection=Yes";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(insertStr);
cmd.CommandText = insertStr;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
connection.Open();
cmd.ExecuteNonQuery();
connection.Close();
}
}
}
{
string insertStr2 = "insert into table " +
"(ID,GLM_ID,NEW_RECORD_IND,ACCURACY) " +
" VALUES " +
"('" + localID + "', null, null, 'Not_Found')";
string connectionString2 = "Data Source=; Initial Catalog=; Trusted_Connection=Yes";
using (SqlConnection connection = new SqlConnection(connectionString2))
{
SqlCommand cmd = new SqlCommand(insertStr2);
cmd.CommandText = insertStr2;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
connection.Open();
cmd.ExecuteNonQuery();
connection.Close();
}
}
}
When I have attempted to use Parallel.Foreach, I had issues with the variables. I'd like to have multiple requests ran, but to retain each instance of the variable per request if that makes sense. I have no way to pass the localID to the API and return it, or that would be ideal.
Is this even possible?
And how would I need to structure this call to achieve what I am after?
Essentially I want to be able to send multiple calls, to speed up the entire process.
EDIT: added the code for GetGlFromAddress(). Yes, I am a newb, so please be kind :)
Put all your data in an array and you could call more than one request at a time, it is best to use multi tasks or Async Methods to call API.
fees_structure has 24 "A" Category rows. But when I try to fetch them and print, it only displays 20 or (21 rows sometimes)
Here is my code:
string catA = "SELECT id,fees_name,amount,fee_category FROM fees_structure where fee_category='A' ORDER BY id";
using (SqlCommand scom = new SqlCommand(catA, con))
{
using (SqlDataReader read = scom.ExecuteReader())
{
read.Read();
if (read.HasRows)
{
while (read.Read())
{
feestableA.Append("<tr>");
feestableA.Append("<td>" + read["fees_name"] + "</td>");
feestableA.Append("<td>" + read["amount"] + "</td>");
feestableA.Append("</tr>");
}
plcfeesA.Controls.Add(new Literal { Text = feestableA.ToString() });
plcfeesA.Dispose();
}
}
}
The read() before the while is suspicious. I suspect that is eating one row.
Another possibility for losing rows would be case-sensitive collations -- if the category could be 'a' (or a variant in another encoding). However, this depends on the default or explicit collations used for the column, database, and server.
I dont think this is a C# issue.
Try this
string catA = "SELECT id,fees_name,amount,fee_category FROM fees_structure where fee_category like '%A%' ORDER BY id";
Just see if your results change
Try this. Watch closely, I removed lines of code that were doing stuff you did not want to be done.
string catA = "SELECT id,fees_name,amount,fee_category FROM fees_structure where fee_category='A' ORDER BY id";
using (SqlCommand scom = new SqlCommand(catA, con))
{
using (SqlDataReader read = scom.ExecuteReader())
{
while (read.Read())
{
feestableA.Append("<tr>");
feestableA.Append("<td>" + read["fees_name"] + "</td>");
feestableA.Append("<td>" + read["amount"] + "</td>");
feestableA.Append("</tr>");
}
plcfeesA.Controls.Add(new Literal { Text = feestableA.ToString() });
}
}
}
I am struggling here trying to understand (after a lot of research a futzing with this) why my MySQL query in C# is returning only one row and the values are wrong.
public static void loadCombinedPaymentExportData(Int32 iFromId = 0)
{
// VERIFIED STATE OF CONNECTION (OUTPUTS "OPEN")
Console.Write(oConn.State+"\n");
// TRIED USING PREPARED STATEMENTS FIRST
//OdbcCommand oCommand = new OdbcCommand("SELECT * FROM v_payment_export_combined_rec WHERE id > ? LIMIT 10", oConn);
//oCommand.Parameters.Add("#ID", OdbcType.Int).Value = iFromId;
// ENDED UP TRYING A MORE DIRECT APPROACH SO I COULD SEE WHAT IS HAPPENING
string sQuery = "SELECT * FROM v_payment_export_combined_rec WHERE id > " + iFromId.ToString() + " LIMIT 10";
// OUTPUTS "SELECT * FROM v_payment_export_combined_rec WHERE id > 0 LIMIT 10"
Console.WriteLine(sQuery);
OdbcCommand oCommand = new OdbcCommand(sQuery, oConn);
using (OdbcDataReader oReader = oCommand.ExecuteReader())
{
// THIS ENTERS INTO THE BLOCK AS HAVING ROWS
if (oReader.HasRows)
{
while (oReader.Read())
{
// THIS ONLY OUTPUTS ONCE AS "Data: 1"
Console.WriteLine("\nData: {0}", oReader.GetString(0));
}
}
else
{
Console.WriteLine("No rows found.\n");
}
oReader.Close();
oCommand.Dispose();
}
}
The issue here is if I run the same exact query on the database using my SQL editor, I have something like 190k+ rows come back from the view, and the lowest id is 8. In fact, I tried adding additional output like:
Console.WriteLine("\nData: {0} | {1} | {2}", oReader.GetString(0), oReader.GetString(1), oReader.GetString(2));
And every single one of them comes back as a 1, which is incorrect as the 2nd column has real text.
Curiously, I had previously done a similar test on another table using this same logic and it worked just fine.
What am I messing up?
[EDIT]
Just tried using the MySQL .NET Connector instead of ODBC and got the same result.
string sQuery = "SELECT * FROM v_payment_export_combined_rec WHERE id > " + iFromId.ToString() + " LIMIT 10;";
MySqlCommand oCommand = new MySqlCommand(sQuery, oConn);
using (MySqlDataReader oReader = oCommand.ExecuteReader())
{
if (oReader.HasRows)
{
while (oReader.Read())
{
Console.WriteLine("\nData: {0}", oReader.GetString(0));
}
}
else
{
Console.WriteLine("No rows found.\n");
}
oReader.Close();
oCommand.Dispose();
}
I will keep investigating here to see if I can figure out what is going on.
So I have been browsing stack overflow and MSDN and cannot find a control (or make sense of the ones I have) to access the data directly of a detailsview. I'm in C# using a .Net WebApplication.
I think what I am looking for is the equivalent in gridview is row.Cells[1].Value can anybody help with the accessor to the DetailsView cells?
What I am trying to do is to access the exact data values I have bound to the DetailsView1
.Text is sufficient for all the numbers and string (only two shown for example) but not for the timestamp MTTS (a datetime) as it lost the milliseconds and the code (SQL query) I use after it cannot find the correct values in the db without the milliseconds. Will I also need to change the way I have bound the data, or some setting to give the bound data millisecond accuracy?
Code example:
Decimal RUN_ID = 0;
DateTime MTTS = new DateTime();
foreach(DetailsViewRow row in DetailsView1.Rows)
{
switch(row.Cells[0].Text)
{
case "RUN_ID":
RUN_ID = Decimal.Parse(row.Cells[1].Text);
break;
case "MTTS":
MTTS = DateTime.Parse(row.Cells[1].ToString());
break;
}
}
I have tried
row.Cells[1].ID = "MTTS";
MTTS = (DateTime)((DataRowView)DetailsView1.DataItem)["MTTS"];
But it does not recognize the MTTS and I am not sure how to set the parameter I have tried a few different things already with no success.
The workaround was messy, essentially I rebuilt the query that gathered the data to the GridView and then I made a function to grab the MTTS directly using LinQ and the parameteres from inside the GridView which assigns the MTTS as a DateTime.
This was in my opinion a bad way of doing things but it worked. I would prefer a better solution.
MTTS = GetMTTS(JOB_PLAN, JOB_NAME,JOB_NAME_ID,RUN_ID,JOB_STATUS);
public DateTime GetMTTS(string JOB_PLAN, string JOB_NAME, string JOB_NAME_ID, Decimal RUN_ID, string JOB_STATUS){
string myEnvName = XXX;
TableName = XXX.ToString();
ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings[myEnvName].ToString();
string thisRUN_ID = RUN_ID.ToString();
cmdText = #"SELECT MTTS FROM " + TableName +
" WHERE JOB_PLAN = '" + JOB_PLAN + "'"
+ " AND JOB_NAME = '" + JOB_NAME + "'"
+ " AND JOB_NAME_ID = '" + JOB_NAME_ID + "'"
+ " AND RUN_ID = '" + thisRUN_ID + "'"
+ " AND JOB_STATUS = '" + JOB_STATUS + "'";
DataSet ds = new DataSet();
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
try
{
SqlCommand SQLcc = new SqlCommand(cmdText,conn);
SqlDataReader reader;
reader = SQLcc.ExecuteReader();
while (reader.Read())
{
MTTS = reader.GetDateTime(0);
}
reader.Dispose();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
}
return MTTS;
}