InvalidCastException in Npgsql - c#

My question is:I want to write the result by suming a column which is money type between two dates.
Code:
using (NpgsqlConnection b = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=postgres;Password=xxxxxxxx;DataBase=deneme;"))
{
try
{
b.Open();
NpgsqlCommand c = new NpgsqlCommand("SELECT SUM(tutar) FROM market where tarih between '" + dateTimePicker1.Value + "' and '" + dateTimePicker2.Value + "'", b);
double toplam = ((double)c.ExecuteScalar());
b.Close();
b.Dispose();
MessageBox.Show(toplam.ToString("n"));
}

Try casting to decimal instead of double.
The Postgres documentation for the "money" type suggests that the output is in the form "$1,000.00" (locale-dependent), in which case you'd need to parse the return value and remove the punctuation before casting.
Also, if that's production code, you'll want to parameterize your SQL instead of appending parameter values in there, otherwise you're open to potential SQL injection attacks and may have performance problems as well.
There are a few threads discussing currency data types in .NET which might also be helpful. Here's one: Does anyone know of a money type in .NET?

Related

name variable from oracle sql string value in c#

This is my first project in c#. I have a little experience in Access VBA. I would like to move my apps over to be stand alone programs. I'm querying a table that has training types and dates. I would like to compare some of the types of training against each other based on the dates they were performed. The three training types are RWT010, RWP000, and RWT010BP. If RWT010BP exists and is newer it is the only one I need. Otherwise I need RWT010 and RWP000. I have figured out how to load the values into variables, but I need to be able to work with them. I would like the name of the dateTime value to be the trainType for the same row. That way I can compare them and output the right combination.
My old Access logic looked like this:
LABEL_DATE: IIf(IsNull([RWT010]),"RWT010BP: " & _
Format([RWT010BP],"Short Date"),IIf([RWT010BP]>[RWT010],"RWT010BP: " & _
Format([RWT010BP],"Short Date"),"RWT010: " & _
Format([RWT010],"Short Date") & " & " & "RWP000: " & _
Format([RWP000],"Short Date")))
This is how far I've gotten in c#:
Console.Write("Enter ID: ");
int idnum = Convert.ToInt32(Console.ReadLine());
string sql = "SELECT EXPID, TYPE, DATE_LATEST FROM TRAINING_TABLE where expid =" + idnum;
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
using (DbDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
int expid = reader.GetInt32(0);
string trainType = reader.GetString(1);
DateTime trainDate = reader.GetDateTime(2);
It looks like the original Access logic has one DB row with three date fields, [RWT010], [RWT010BP], and [RWP000]. But in Oracle that's been normalized so you're getting back multiple rows, each of which has a a datetime field named [DATE_LATEST], and then name field called [TYPE] that's equal to "RWT010", "RWT010BP", or "RWP000".
And you were thinking, you want to handle those RWP000 date values by name, just like in Access. You were right, that's the clearest way to do it, and I'll show you how. I misunderstood what you were asking.
One way to do this would be to write an Oracle stored procedure that duplicates the Access logic. That's not the question you asked, but it's a legitimate way to do it. However, it would be more complicated than the Access version due to the change in the database, and anyway I haven't written Oracle SQL in years and I don't have an Oracle server handy to give me arbitrary, cryptic syntax errors about semicolons and whitespace.
So what I'm going to do is write a loop in C# to grab the datetimes from the DB rows and put them in local variables, and then duplicate the Access logic in C# using those variables instead of fields. It'll be a little verbose compared to the Access version, but sometimes that's how it goes.
int idnum = Convert.ToInt32(Console.ReadLine());
string sql = "SELECT EXPID, TYPE, DATE_LATEST FROM TRAINING_TABLE where expid =" + idnum;
// I don't know how you're using this so I'll just declare it here
// and leave that to you.
String dateLabel = "";
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
using (DbDataReader reader = cmd.ExecuteReader())
{
DateTime? RWT010 = null;
DateTime? RWT010BP = null;
DateTime? RWP000 = null;
// No need to check reader.HasRows. If it has no rows, reader.Read()
// will return false the first time, that's all.
while (reader.Read())
{
// Doesn't look to me like expid is used
//int expid = reader.GetInt32(0);
string trainType = reader.GetString(1);
DateTime trainDate = reader.GetDateTime(2);
switch (trainType) {
case "RWT010":
RWT010 = trainDate;
break;
case "RWT010BP":
RWT010BP = trainDate;
break;
case "RWP000":
RWP000 = trainDate;
break;
}
}
if (RWT010 == null || RWT010BP > RWT010) {
dateLabel = String.Format("RWT010BP: {0:d}", RWT010BP);
} else {
dateLabel = String.Format("RWT010: {0:d} & RWP000: {1:d}", RWT010, RWP000);
}
}
The original logic was this:
If RWT010 isn't null,
Do A
Otherwise, if RWT010BP > RWT010
ALSO do A
But if none of the above,
Do B
The first two branches do the exact same thing, so we can condense them both into one branch.
"Don't Repeat Yourself", as they say. You don't want to return to this code a year from now, wonder if those two lines were required to be the same, and then guess wrong or else not notice that they are the same, and only change one or the other. It's just a mess.
If you're not familiar with String.Format(), there's a lot to it. In the first argument string, {0} means "insert the second argument here"; {1} means "insert the third", and so on. The ":d" inside the curly braces is optional; it means to pass "d" as format information to the value its inserting. DateTime will interpret that "d" to mean "Short Date". You could also do it like this:
dateLabel = String.Format("RWT010BP: {0}", RWT010BP.Value.ToShortDateString());
Or like this:
dateLabel = "RWT010BP: " + RWT010BP.Value.ToShortDateString();
I have to use RWT010BP.Value in that line instead of just RWT010BP because RWT010BP is declared with a ? after it. That makes it a "nullable" value. A regular DateTime can't be null, but we need to accommodate nulls here.
If you're using C#6, you can do it like this, which I prefer. I didn't use it above because I don't know what version of C# you're on. Always prefer the least amount of "noise" cluttering up the code.
dateLabel = $"RWT010BP: {RWT010BP:d}";
That's the same ":d" as in String.Format("{0:d}", ...) above.
One more thing: idnum is an int, but don't ever concatenate a string value into a SQL string. That's a massive security vulnerability and people here will (rightly, I'm afraid) give you a very hard time for even contemplating it.
Use OracleCommand.Parameters instead, as shown in this answer. I would have used that even in this case, personally, just as a conditioned reflex.

How could I compute the number from the database and show it to the Label everytime I change the combo box value?

I have a database values that I have to show to the label as the person salay per hour.
My code is here:
sqc = con.CreateCommand();
string query5 = "SELECT [SalaryGrade] FROM tbl_gradestep where GradeNumber =" + cmbGradeNumber.SelectedItem.ToString() + " and StepNumber =" + cmbStepNumber.SelectedItem.ToString() + "";
sqc.CommandText = query5;
sda = new SqlDataAdapter(sqc);
dt = new DataSet();
sda.Fill(dt);
if(dt.Tables[0].Rows.Count > 0)
{
lblSalary.Text = dt.Tables[0].Rows[0]["SalaryGrade"].ToString();
lblHour.Text = (dt.Tables[0].Rows[0]["SalaryGrade"].ToString()/22/8);
}
con.Close();
The computation is database values divided by 22 then divide it again by 8.
This is, quite explicitly, a string:
dt.Tables[0].Rows[0]["SalaryGrade"].ToString()
A string is just text. You don't perform math on it. If this string is guaranteed to be an integer, you can convert it directly:
lblHour.Text = (int.Parse(dt.Tables[0].Rows[0]["SalaryGrade"])/22/8);
If it's not guaranteed, then you might want some error checking first:
int salaryGrade;
if (!int.TryParse(dt.Tables[0].Rows[0]["SalaryGrade"], out salaryGrade))
{
// wasn't an integer, handle the error condition here
}
lblHour.Text = (salaryGrade/22/8);
Note: Integer division is going to result only in an integer. If you're looking for decimal values, you're going to want to convert your numbers to something with decimal precision. (Such as decimal, which also has .Parse() and .TryParse() methods of course.) Otherwise you may just end up with zeroes and not know why.
the error is "Operator "/" cannot be applied to operands of type
"string" and "int"
The error message is pretty informative. The following
dt.Tables[0].Rows[0]["SalaryGrade"].ToString()
is a string and 22 is an int literal. You can't apply the division operator to them. How could you divide a string with an int, it is a meaningless operation. That you need is something like the following:
((decimal)dt.Tables[0].Rows[0]["SalaryGrade"])/(22*8)
First you cast the SalaryGrade to a decimal and then you divide this with the hours.
Note: In the above line I have assumed that SalaryGrade can be casted to a decimal. If the cast is not possible an exception would be thrown. So a more safe approach it would be to try to use the TryParse method of decimal:
decimal salaryGrade;
if(decimal.TryParse(dt.Tables[0].Rows[0]["SalaryGrade"], out salaryGrade))
{
var salaryPerHour = salaryGrade/(22*8);
}
The error occurs because you cannot use a math operation in a string!
Try this: declare an int variable and assign to it the data from the table and then use the int in the "label.Text" assign converting that int to string.
Hope helped ya!
Thank you guys for helping me out here. I read all your answers then modify my code and boom. Works like a charm.
decimal salaryGrade;
sqc = con.CreateCommand();
string query5 = "SELECT [SalaryGrade] FROM tbl_gradestep where GradeNumber =" + cmbGradeNumber.SelectedItem.ToString() + " and StepNumber =" + cmbStepNumber.SelectedItem.ToString() + "";
sqc.CommandText = query5;
sda = new SqlDataAdapter(sqc);
dt = new DataSet();
sda.Fill(dt);
if(dt.Tables[0].Rows.Count > 0)
{
lblSalary.Text = dt.Tables[0].Rows[0]["SalaryGrade"].ToString();
if (decimal.TryParse(dt.Tables[0].Rows[0]["SalaryGrade"].ToString(), out salaryGrade))
{
}
lblHour.Text = Convert.ToDecimal(salaryGrade / 22 / 8).ToString("0.00");
}
con.Close();
}

convert a double with a comma to a variable with a point to use in sql statement

I use my double in a select statement:
code:
SqlCommand command = new SqlCommand("SELECT min(Score) FROM "+ table +" WHERE [" + sportEvent + "] < (#result);", connect);
command.Parameters.AddWithValue("#result", result);
everything works fine if double result is an integer but not if result is a comma number (example 11,34) --> it should be 11.34 to work (point instead of comma)
How can I change a double 11,34 into 11.34 ?
It appears that your code sets a string parameter as a constraint for a DB value of numeric type, letting the database do the conversion. This is not a good idea, because it takes control away from your program: should DBA decide to reconfigure your backend database to "understand" commas instead of dots, your program will stop working!
Currently, your double is in a locale-specific format. You need to parse it using the locale-specific format provider, and then set the value that you get back from the parser as the parameter of your SQL query. Assuming that the current culture is one that is using commas as decimal separator, you can do this:
command.Parameters.AddWithValue(
"#result"
, double.Parse(s, CultureInfo.CurrentCulture)
);
You can use this
result.ToString(CultureInfo.InvariantCulture)
You could try changing the variable into string:
result.ToString().Replace(',','.');
This will replace the comma with a dot.
If result is Double then:
command.Parameters.Add("#result", SqlDbType.Float).Value = result

working with access database update statment not working

string date=
DateTime.Now.ToString("d.M.yyyy",System.Globalization.DateTimeFormatInfo.InvariantInfo);
String MyString = #"UPDATE cas SET Odhod= '" + label1.Text + "'
WHERE sifra = " + textBox1.Text + " and Datum = "+date+"";
When I do thise update without Datum it works, but with Datum doesn't work. I'm connected to accesss database, and Datum field type in table is date/time Guys please help.
Hire is the program: https://www.dropbox.com/s/hx4zduvul8mh2uy/8.4.zip
Pictre of problem: http://img43.imageshack.us/img43/5189/errorbh.jpg
As usual, using string concatenation brings in a lot of trouble.
(Sql Injection, Parsing problems)
Just use parametrized queries
string MyString = #"UPDATE cas SET Odhod= ? WHERE sifra = ? and Datum = ?";
using(OleDbConnection cn = new OleDbConnection(connectionstring))
using(OleDbCommand cmd = new OleDbCommand(MyString, cn)
{
cn.Open();
cmd.Parameters.AddWithValue("#p1", label1.Text);
cmd.Parameters.AddWithValue("#p2", textbox.Text);
cmd.Parameters.AddWithValue("#p3", Convert.ToDate(date));
cmd.ExecuteNonQuery();
}
Of course, the Date value stored in the Datum field should be exactly like the date passed in parameter #p3. Sometime it is good to add also the time value to your date
string date= DateTime.Now.ToString("d.M.yyyy 00:00:00", ......);
It is likely that the value in your Datum column doesn't match your date value. What are the values of your Datum column? Is there some time associated with it (EG: 1:32pm)?
There are also a couple of other issues with this update statement. You should be either using an ORM or parameterized queries in order to avoid SQL injection.
You can use parameter approach. In that case, you will be able to define the field type before you run the query.
The reason is the way Microsoft Access actually reads the DateTime a particular way. When you utilized concatenation it can introduce problems.
Access can only store Date / Time in these manners:
Valid Date: -657, 434 (January 1, 100 A.D.) to 2,958,465 (December 31, 9999 A.D.)
Valid Time: .0 (00:00:00) to .99999 (23:59:59)
It can be formatted in the following way:
Stored Value (Double Number) = Default Format (General Date) = Custom Format
36296.0 = 5/15/99 = 05/15/1999 12:00:00 AM
So you'll have to keep in mind the limitations of Microsoft Access; because SQL does read the dates slightly differently and can store them slightly different. The slightest break in context can deeply affect the outcome.
The easiest approach would be to Query based on Parameters. That way you don't concatenate invalid syntax for Access possibly. This will alleviate some of the issues.
Natively access utilizes these functions:
Add an associated date to the time comparison:
var1 = #1/1/99 2:01:00 PM#
var2 = DateAdd("n", 10, var1)
? var2 = #1/1/99 2:11:00 PM#
Convert the time values to string data types before you compare them:
var1 = #2:01:00 PM#
var2 = DateAdd("n", 10, var1)
? CStr(var2) = CStr(#2:11:00 PM#)
Use the DateDiff() function to compare precise units such as seconds:
var1 = #2:01:00 PM#
var2 = DateAdd("n", 10, var1)
? DateDiff("s", var2, #2:11:00 PM#) = 0
So a native Access Query would look like such:
UPDATE [dbo].[Customer]
SET [dbo].[Customer].[InvoiceDate] = #1/1/99 2:11:00 PM#;
As you can see it tries to work like SQL, but it isn't SQL. So by creating a Parameter based Query you can ensure the valid syntax is being implemented into the Access Database. Steve posted a nice example, so I won't post code example. But hopefully this helps you understand the differentiation between them.

Decimal.Parse Issue

I have a problem with decimal parse, I know this question been asked a loot, but none of the solution worked for me, and i've been stuck in here for two days now.
My problem is my CultureInfo is set to fr_Fr and when i put the code below an error shows caused with comma that separates decimal instead of period.
double entree = Convert.ToDouble(row["entree"]);
double sortie = Convert.ToDouble(row["sortie"]);
int id_mw = Convert.ToInt32(row["mouvment_w_id"]);
qte_Stock += entree - sortie;
decimal qte_s ;
MessageBox.Show("" + CultureInfo.CurrentCulture);
qte_s = Decimal.Parse(Convert.ToString(qte_Stock), NumberStyles.Number ^ NumberStyles.AllowThousands);
MessageBox.Show("" + qte_s);
qte.CommandText = "Update tcpos.dbo.Warehouse_mouvement set qte_stock= " + qte_s + " where mouvment_w_id = "+id_mw;
qte.ExecuteNonQuery();
What's the type of qte_Stock
...
it was a decimal
...
Then you have your answer since you want to make it a decimal.
Pass the decimal as Parameter to prevent sql-injection and this issue(decimal separator).
For example(assuming SQL-Server as rdbms):
using(var con = new SqlConnection(connectionString))
using(var cmd = new SqlCommand("Update tcpos.dbo.Warehouse_mouvement set qte_stock=#qte_stock where mouvment_w_id = #mouvment_w_id", con))
{
cmd.Parameters.AddWithValue("#qte_stock", qte_stock);
cmd.Parameters.AddWithValue("#mouvment_w_id", id_mw);
con.Open();
cmd.ExecuteNonQuery();
}
qte_s = qte_Stock.ToString ("#.#", System.Globalization.CultureInfo.InvariantCulture);
This will solve, if your problem is because in your culture (like mine), the comma seaparates decimals, and not thousands.
Also, use parameterized SQL, like Damien_The_Unbeliever and Jonh Skeet said.

Categories

Resources