trying to read sql statement c# - c#

string queryStr = "select max(patient_history_date_bio) " +
"as med_date, medication_name from biological where " +
"(patient_id = " + patientID.patient_id + ") " +
"group by medication_name;";
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand(queryStr, conn))
{
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
int count = 0;
while (rdr.Read())
{
MessageBox.Show("test");
med.medication_date[count] = new DateTime();
med.medication_date[count] = DateTime.Parse(rdr["med_date"].
ToString());
MessageBox.Show("test2");
med.medication_name[count] = rdr["medication_name"].ToString();
count++;
}
}
conn.Close();
}
so i'm trying to read this sql statement. "test" message box displays, but not "test2". I tried running the sql statement in VS by itself (in the server explorer), and the sql statement works. it gives me what i want. but somehow, the code doesn't work... does anyone see why?

Assuming patient_id is some sort of integer (or is it a Guid), my assumption is that the current culture of your program is causing the ToString method call on int to be formatted in a way that is returning something that the parser can't parse (e.g. "1,234,567").
Generally, the way you are executing this statement is not a best-practice. While you might not be susceptible to injection attacks if the id is indeed an int (you are most definitely open to them if it's a string), you generally want to parameterize the queries.
The reason for this is not only to protect against injection attacks, but because it will properly format the parameter in the query string according to the type.
Another thing to point out about your code is how you are retrieving the values from the reader. You are effectively calling ToString on the DateTime instance, then calling Parse on the string to get a DateTime back.
This effectively burns cycles. All you need to do is cast (unbox in the case of value types) the value back.
So where you have:
med.medication_date[count] = DateTime.Parse(rdr["med_date"].
ToString());
You should have:
med.medication_date[count] = (DateTime) rdr["med_date"];
All that being said, as to why the second message box is not showing, my first guess is that you are executing this in an event handler in a Windows Forms application, and that an exception is being thrown.
I think that what you will find is that if medication_date is an array, then it hasn't been initialized and you are getting a NullReferenceException or something about the array index being out of bounds.

What is med.medication_date?
If it is an array, maybe it hasn't been initialized yet.
If it is a list, you should assign to it using med.medication_date.Add(value);
Alternatively, as everyone else is saying, the date time conversion may be at fault. Try replacing
MessageBox.Show("test");
With
MessageBox.Show(rdr["med_date"].ToString());

you should direct your debug stuff to the output window...it's muche easier to follow the flow.
system.diagnostics.debug.writeline(rdr["med_date"].ToString());

Without more info, it looks like the line
med.medication_date[count] = DateTime.Parse(rdr["med_date"].ToString());
throws an exception due to an unrecognised date, and the exception is being swallowed by a handler higher up.

Related

Comparing a string to it's equal in a database returns false

I would like to find in my database which line has its "path" field's value equal to the string "c:\something\somethingelse\anotherthing.thing".
I found the line by browsing and copied it's content, before making an SQL request:
On C# side, my code looks like this:
EDIT: Due to #CompuChip 's comment, I edited my line for something that I hope may be better
String MyPath = "c:\\something\\somethingelse\\anotherthing.thing"
MyPath = String.Format("select * from x where path = '{0}'", MyPath);
Then I called the method to create and send the request to my database but I got an exception
I tried the request itself on my database but even if I copied the exact value it couldn't find the line:
Here's what I tried:
Select * from x where path = "c:\something\somethingelse\anotherthing.thing"
Select * from x where STRCMP(path, "c:\something\somethingelse\anotherthing.thing") = 0
Is there a good way to compare the strings correctly and find my line ?
Thank you for your time and have a nice day.
Edit 2: I also tried getting all my lines in C# then comparing them one by one.
It worked, but with 10K+ lines it's beginning to consume resources
Please, please, please don't construct your SQL queries like that.
The proper solution is using parameters, as explained here:
Why do we always prefer using parameters in SQL statements?
Your C# code would become something like this (adapted from the linked answer):
string sql = "select * from x where path = #path";
using (SqlCommand command = new SqlCommand(sql, connection))
{
var pathParam = new SqlParameter("path", SqlDbType.VarChar);
pathParam.Value = #"c:\something\somethingelse\anotherthing.thing";
command.Parameters.Add(pathParam);
var results = command.ExecuteReader();
}
or, for MySql,
string sql = "select * from x where path = ?path";
using (MySqlCommand command = new MySqlCommand(sql, connection))
{
var pathParam = new MySqlParameter("path",
#"c:\something\somethingelse\anotherthing.thing");
command.Parameters.Add(pathParam);
var results = command.ExecuteReader();
}
Assuming that you (have checked that you) actually have a matching record in the database, the problem is likely either with the quoting, or the escaping of the value.
If that is indeed the case, using parameters to retrieve the record is not only the safe thing to do, it will also make your quoting or escaping problem a non-issue.
It appears I have to do the same thing I do in C# and put two backslashes instead of one in the path, even in MySQL
Furthermore I also had to construct correctly my requests as #CompuChip hinted.
Thank you all for your help !

Troubleshooting "Data type mismatch in criteria expression." during MS Access Insert.Into

I'm creating a basic customer inventory application, and when converting the code from using SQL Server to using MS Access (which I'm quite a bit less versed in), I ran into a "Data type mismatch" error when trying to do a basic insert.
I've looked into several similar questions here, and double checked the msdn syntax guide, but I can't find a reason why the script I've written would generate that error. I changed my code several times to try and ensure proper data type (ending up with what I have below with explicit typing and adding the value later). I've actually even taken the string and pasted it into MS Access (sans white space and double quotes), and it seems to work just fine with the values given. At this point, I'm really and truly stumped, and I'm wondering if it might just be a quirk with the Oledb adapter? Any help would be appreciated. Thanks.
// SQL query defined elsewhere:
public static readonly string sqlAddCustomerNotes = "INSERT INTO CustomerNotes (Customer_ID, Notes, NotesDate) "
+ "VALUES(#Customer_ID, #Notes, #NotesDate);";
// end sql query
// data access function
public static void addNotes(int customerID, string notes, DateTime notesDate)
{
string query = Scripts.sqlAddCustomerNotes;
using (
OleDbCommand dbCommand = new OleDbCommand()
{
Connection = new OleDbConnection(ConnectionAccess.connString),
CommandType = CommandType.Text,
CommandText = query,
Parameters =
{
new OleDbParameter("#Customer_ID", OleDbType.Integer),
new OleDbParameter("#Notes", OleDbType.LongVarChar),
new OleDbParameter("#NotesDate", OleDbType.DBTimeStamp)
}
}) // end using parenthetical
{ // begin using scope
dbCommand.Parameters[0].Value = customerID;
dbCommand.Parameters[1].Value = notes;
dbCommand.Parameters[2].Value = notesDate;
foreach (OleDbParameter param in dbCommand.Parameters)
{ // replace ambiguous null values with explicit DBNulls.
if (param.Value == null)
{
param.Value = DBNull.Value;
}
}
dbCommand.Connection.Open();
int rowsAffected = dbCommand.ExecuteNonQuery();
dbCommand.Connection.Close();
Console.WriteLine($"Rows affected: {rowsAffected}");
}
} // end addCustomerNotes
/*
table "CustomerNotes" has the following columns:datatypes
CustomerNotes_ID: AutoNumber
Customer_ID: Number
Notes: Memo
NotesDate: Date/Time
CreateDate: Date/Time
test case (in code) was:
#Customer_ID = 5
#Notes = "customer might change last name to simpson."
#NotesDate = {6/26/2019 12:05:39 PM}
*/
It probably is a date, not a timestamp:
new OleDbParameter("#NotesDate", OleDbType.DBDate)
Considering June7's comment about delimiters, it seems the issue lies in some issue inherent to the OleDbParameter type. In SQL Server terms, I do want DateTime (not Date), but representing it as a DBTimeStamp seems to make it unrecognizable by Access.
For the time being, I've sent the date as a VarChar and allowed Access to convert it however its internal engine sees fit. It feels/seems wrong, but it does, in fact, solve the problem.
Parameters =
{
new OleDbParameter("#Customer_ID", OleDbType.Integer),
new OleDbParameter("#Notes", OleDbType.LongVarChar),
new OleDbParameter("#NotesDate", OleDbType.VarChar)
}
EDIT: Just saw June7's latest comment, and there was in fact, an answer in another thread. OleDbType.DBDate doesn't do what I want, but OleDbType.Date does.

Invalid Cast Exception was unHandled MySQL

When I run the program I get this error:
Invalid Cast Exception was unHandled
This is part of form for fill a database, and visual Studio marks the int cast as the error
MySqlConnection conectar = new MySqlConnection("server=127.0.0.1; database=gymne; Uid=root; pwd=0000000000;");
using (MySqlCommand sqlCommand = new MySqlCommand("SELECT COUNT(*) from Socios where Nombre like #pNombre AND Apellido like #pApellido", conectar))
{
conectar.Open();
sqlCommand.Parameters.AddWithValue("#pNombre", txtNombre.Text);
sqlCommand.Parameters.AddWithValue("#pApellido", txtApellido.Text);
int UsuarioExiste = (int)sqlCommand.ExecuteScalar();//<----"error here"
if (UsuarioExiste > 0)
{
MessageBox.Show("El Socio ya existe!!", "No Guardado", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
The problem is the ExecuteScalar that in MySql returns an Int64 not an Int32. So the invalid cast when you use an explicit cast
With a conversion your error should go away
int UsuarioExiste = Convert.ToInt32(sqlCommand.ExecuteScalar());
You are not alone to fall in this problem
Of course everything that has been said in the answer from Mr Soner Gönül is still applicable and should be done ASAP.
I strongly suspect, you parameterize your LIKE part in a wrong way. You need to use %..% at least to figure out you try to get values that includes those string. Like;
sqlCommand.Parameters.AddWithValue("#pNombre", "%" + txtNombre.Text + "%");
sqlCommand.Parameters.AddWithValue("#pApellido", "%" + txtApellido.Text + "%");
Don't use AddWithValue as much as you can. It may generate unexpected and surprising results sometimes. Use Add method overload to specify your parameter type and it's size.
Also use using statement to dispose your connection and command automatically instead of calling (in somewhere maybe in your code) Close or Dispose methods manually.
By the way, be aware, COUNT(*) returns BIGINT in MySQL and this type mapped with Int64 in .NET side. As Steve mentioned, you can parse this value instead of casting like;
int UsuarioExiste = int.Parse(sqlCommand.ExecuteScalar());
or you can define your UsuarioExiste as long which seems more consistent(?) I think.
long UsuarioExiste = (long)sqlCommand.ExecuteScalar();

Delete query won't work

I'm trying to remove a row from the database that has the same ART as is selected in the combobox. I had it working before but when I changed the database it was supposed to delete it from it stopped working and gave me a error message. I did change the database connection etc acording to the database change.
The error message (Hoping image works)
I don't know why it says "conversion failed when converting the varchar value 'R06018' to data type int" since I don't have a value of R06018 anywhere in the code, nor is it the selected row.
the code I tried after the delete stopped working, it's just the delete without any thing extra (I know it doesn't dispose but the program crashes when it tries to read, and it's just for finding the issue)
try
{
SqlCommand inkoopartdelete = new SqlCommand("delete from ART where ART=" + artnr.SelectedItem + "", Connectie.connMEVO);
drMEVO = inkoopartdelete.ExecuteReader();
MessageBox.Show(this.artnr.SelectedItem + " verwijderd.");
}
catch (Exception e) { MessageBox.Show("" + e); }
The old code after I changed the db connection (set as comment since I tried a smaller bit of code for the delete)
//SqlCommand inkoopdelete = new SqlCommand("delete from ART where ART=#art", Connectie.connMEVO_ART);
//inkoopdelete.Parameters.Add("#art", SqlDbType.VarChar).Value = artnr.SelectedItem;
//drMEVO = inkoopdelete.ExecuteReader();
//try
//{
// while (drMEVO.Read())
// { }
// MessageBox.Show(this.artnr.SelectedItem + " verwijderd.");
//}
//catch (SqlException v)
//{
// MessageBox.Show("" + v);
//}
//inkoopdelete.Dispose();
I hope any of you could help me, since I can't find the issue.
Found the issue, see accepted awnser for error in test code, real error seems to be me reading over a part of the code -_- ...srry
If the ART field is of type nvarchar then, if you really want to use string concatenations, you should enclose your string value in single quotes and write
SqlCommand inkoopartdelete = new SqlCommand(#"delete from ART
where ART='" + artnr.SelectedItem + "'", Connectie.connMEVO);
That's a valid enough reason to revert as soon as possible to use a parameterized query as you have initially. Other reasons to avoid this is the fact that if your value has an embedded single quote the Whole text becomes syntactically wrong. And, finally, string concatenation is the open door for Sql Injection Attacks
A last note. If you want to execute a query like DELETE/INSERT or UPDATE do not use ExecuteReader. It works, but it is not necessary to build an SqlDataReader for that kind of queries. Just use
int affectedRows = inkoopartdelete.ExecuteNonQuery();
Change below statement :
You have to give single quote.
SqlCommand inkoopartdelete = new SqlCommand("delete from ART where ART='" + artnr.SelectedItem + "'", Connectie.connMEVO);

How to get multiple rows from stored procedure?

I have a method for adding values to the database for all operations.
If this is selected from the database and this select return more rows from the database,
how can I get the rows and store in an array?
This is the method code :
public void ExcuteProcedure(string procName, List<SqlParameter> procparams)
{
try
{
SqlConnection mycon = new SqlConnection(connectionString);
mycon.Open();
SqlCommand mycom = new SqlCommand();
mycom.Connection = mycon;
mycom.CommandText = procName;
mycom.CommandType = CommandType.StoredProcedure;
foreach (var item in procparams)
{
SqlParameter myparm = new SqlParameter();
myparm.ParameterName = item.ParameterName;
// myparm.SqlDbType = item.SqlDbType;
myparm.Value = item.Value;
mycom.Parameters.Add(myparm);
}
var n= mycom.ExecuteScalar();
mycon.Close();
}
catch (SqlException e)
{
Console.WriteLine("Error Number is : " + e.Number);
Console.WriteLine("Error Message is : " + e.Message);
}
}
You need to call mycom.ExecuteReader(), which will give you a SqlDataReader which can read through the results.
Call Read() to advance through the rows.
It never ceases to amaze me the number of times I see devs trying to abstract away simple database connectivity; and the myriad of ways they inevitably screw it up.
The following may sound mean, but it needs said:
Clean up your code, it leaks like a sieve. Using clauses around the connection and command objects are pretty much mandatory. As it stands if you forget a single parameter or put in a bad value you will leak connections. Once the connection pool is filled up your app will crash in all sorts of interesting, and usually hard to debug, ways.
Next, if you aren't sure how to properly get records back from a database then you probably shouldn't try to abstract the code calling your procedures. Either use a lightweight ORM like Dapper or learn how what you are doing will ultimately involve a lot of extraneous code that the next developer on your project will want to rip out.
/rant over.
Getting back to the question: ExecuteScalar returns a single value. You need to use ExecuteReader. I'd suggest that you simply take the results of the reader, stuff it into a datatable and pass that back to the calling code.
var n = mycom.ExecuteScalar();
Scalar: an atomic quantity that can hold only one value at a time
Return a DataReader instead, and iterate through its rows
Fill a DataSet by using a DataAdapter (this is more appropriate if you have multiple tables in the result set).

Categories