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.
Related
My web api list contains a datetime field which includes the following format
"2018-09-17T09:35:01.5842031 05:30" but it should be "2018-09-17 09:35" however, Processtime is datetime Column and this column values automatically converts to "2018-09-17T09:35:01.5842031 05:30" though I send it as datetime field
How do I accomplish this task when inserting the value.
Insert query is like this
foreach (var item in talleyheaderlist)
{
SQL = "insert into TalleySheetHeader (Processtime) values (#ProcessStartTime)";
SqlCommand com = new SqlCommand(SQL, con);
com.Parameters.AddWithValue("#ProcessStartTime", item._TsStartTime );
con.Open();
com.ExecuteNonQuery();
con.Close();
}
and i use C# code to insert to SQL server 2014 DB
parameter Insertion
com.Parameters.AddWithValue("#ProcessStartTime", item.ProcessStartTime);
I appreciate somebody's help...
"my...list contains a datetime field which includes the Following format "2018-09-17T09:35:01.5842031 05:30""
Let me stop you there. The quoted value is a string representation of a value. That string might have come from a DateTime or (given that 05:30 could be a TimeZone) a DateTimeOffset, but it isn't that type any more.
You need to stop using AddWithValue, because (as it explains on that page) when you do...
com.Parameters.AddWithValue("Processtime", item._TsStartTime );
and
com.Parameters.AddWithValue("#TalleyProcessStartTime", _valstartedtiem);
(both of which come from your answer to your question) both variables are strings, but your SQL wants that parameter to be a datetime, so you're letting/forcing SQL to make that conversion for you.
So you either need to get the original value (before it became a string), or convert the string back to a DateTime, using DateTime.Parse, probably using the overload with DateTimeStyles, using the RoundTripKind. Although that ISO format should have a plus or minus for the TimeZone; but it might work.
Now that you have a datetime, explicitly pass it as a SqlDbType.DateTime, as shown on the AddWithValue link article. Now you can remove the convert(...,121) from the Sql, since the parameter is now the correct type.
Bonus tips: SqlConnection and SqlCommand are both IDisposable so each should be in a using block. Once you've done that, you don't need to call Close as the implicit Dispose will call it when it exits the using block.
Thanks for every One's help. but i found the answer after a while.
basically ths datetime format is in ISO 8601 format in that case, I had to convert it as follows,
foreach (var item in talleyheaderlist)
{
SQL = "insert into TalleySheetHeader (Processtime) values convert(char(22),#Processtime,121)"; SqlCommand com = new SqlCommand(SQL, con); com.Parameters.AddWithValue("Processtime", item._TsStartTime );
con.Open();
com.ExecuteNonQuery();
con.Close();
}
and to seperately split the date & time to remove 't' and then re-join
string[] Separate = item._TsStartTime.Split(' ');
string a = Separate[0];
string[] _dateTime = a.Split('T');
string _startdate = _dateTime[0];
string _starttime = _dateTime[1];
string _valstartedtiem = _startdate +' ' + _starttime;
com.Parameters.AddWithValue("#TalleyProcessStartTime", _valstartedtiem);
however , there may be other easy Solutions but so far this worked for me. if there is any easy Solution Please do share.
You can simply do this:
string dateTime = "2018-09-17T09:35:01.5842031 0530";
var date = DateTimeOffset.ParseExact(dateTime.Replace(' ','-'), "yyyy-MM-dd'T'HH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
More on how 'T' and 'z' works is here:
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a003169814.htm
Please insert following logic in your global.asax.cs file
protected void Application_BeginRequest(Object sender, EventArgs e)
{
CultureInfo newCulture = (CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
newCulture.DateTimeFormat.ShortDatePattern = "your date format";
newCulture.DateTimeFormat.DateSeparator = "/";
Thread.CurrentThread.CurrentCulture = newCulture;
//ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder());
}
code for Inserting into database
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ $"VALUES(\""{name}\",\""{answerList[0]}\",\"{answerList[1]}\",\""{answerList[2]}\",\"{answerList[3]}\",\"{name}\"};";
I am getting error in this line as "; expected":
+ $"VALUES(\""{name}\",\""{answerList[0]}\",\"{answerList[1]}\",\""{answerList[2]}\",\"{answerList[3]}\",\"{name}\");";
In three places, you have an unescaped second double quote, which ends the quoted string right there:
\""{name
and
\""{answerList[0]
and
\""{answerList[2]
Those break your C#, and if you escaped them, they'd break your SQL. So don't do that. Almost certainly, you should be using single quotes rather than double quotes as well (thanks Icarus):
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ $"VALUES('{name}','{answerList[0]}','{answerList[1]}','{answerList[2]}','{answerList[3]}','{name}'};";
However, that's very bad coding style. It's vulnerable to SQL injection attacks, it'll crash if one of your answers happens to have an apostrophe in it, and putting quoted or even just matched quotes in a string is highly error-prone, as you've discovered.
So start over and rewrite the code using parameters, which resolve all of these issues cleanly and simply:
SqlCommand cmd = new SqlCommand();
// ...etc.
cmd.Parameters.Add("#name", SqlDbType.NVarChar);
cmd.Parameters.Add("#answerList0", SqlDbType.NVarChar);
// ...etc.
cmd.Parameters["#name"].Value = name;
cmd.Parameters["#answerList0"].Value = answerList[0];
// ...etc.
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ "VALUES(#name,#answerList0,#answerList1,#answerList2,#answerList3,#name};";
try to build the string using like this:
$#"query = ""INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)""
""VALUES(""{name}"",""{ answerList[0]}"",""{answerList[1]}"",\""{
answerList[2]}"",""{answerList[3]}"",""{name}""};"";";
never use a + when building strings, because it will evaluate both and then append them to a third instead of creating just 1 string.
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.
Im importing a csv to my sql server table using the following code
SqlCommand nonqueryCommand = myConnection.CreateCommand();
nonqueryCommand.CommandText =
"INSERT INTO MYTABLE VALUES(#num1, #num2,#num3,#num4)";
nonqueryCommand.Parameters.Add("#num1",SqlDbType.Decimal);
nonqueryCommand.Parameters.Add("#num2", SqlDbType.Decimal);
nonqueryCommand.Parameters.Add("#num3", SqlDbType.Decimal);
nonqueryCommand.Parameters.Add("#num4", SqlDbType.Decimal);
nonqueryCommand.Parameters["#num1"].Value = crntRecord[0];
nonqueryCommand.Parameters["#num2"].Value = crntRecord[1];
nonqueryCommand.Parameters["#num3"].Value =crntRecord[3];
nonqueryCommand.Parameters["#num4"].Value = crntRecord[4];
nonqueryCommand.ExecuteNonQuery();
where the parameter 3 and 4 are of type decimal(9,6) in the DDL when i execute the code at ExecuteNonQuery i get the following exception
Failed to convert parameter value from a String to a Decimal.
please help me find out the problem tnx.
EDIT
the value in the crntRecord[3] looks like
Assuming that crntRecord is an array of strings, you need to parse the strings to a decimal first.
Ex:
nonqueryCommand.Parameters["#num3"].Value = decimal.Parse(crntRecord[3].ToString());
Note that this will throw an exception if crntRecord[3] is not parseable to a decimal; if that's a situation that could occur, look into decimal.TryParse() instead.
Edited to use safer parsing methods
Your strings have surrounding quotes that you need to strip off. Try
decimal num3;
bool isDecimal = decimal.TryParse(crntRecord[3].Trim(new []{'\"'}), out num3);
if(isDecimal)
nonqueryCommand.Parameters["#num3"].Value = num3;
I would recommend using this method for all of your decimals, which would mean putting this logic in a reusable function would be a rise refactoring.
try with
nonqueryCommand.Parameters["#num1"].Value = Convert.ToDecimal(crntRecord[0]));
nonqueryCommand.Parameters["#num2"].Value = Convert.ToDecimal(crntRecord[1]);
nonqueryCommand.Parameters["#num3"].Value =Convert.ToDecimal(crntRecord[3]);
nonqueryCommand.Parameters["#num4"].Value = Convert.ToDecimal(crntRecord[4]);
Use
nonqueryCommand.Parameters.AddWithValue("#num1", crntRecord[0]);
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?