Convert SQLite timestamp to DateTime in C# - c#

I am trying to assign a property of an object the value of a column containing a timestamp in SQLite. Currently the property is of type DateTime, but I've tried string type with no change. I've tried using Convert.ToDateTime(rdr.GetString(5).ToString()) with no change.
Here's the exception I get:
System.InvalidCastException: Specified cast is not valid.
at System.Data.SQLite.SQLiteDataReader.VerifyType(Int32 i, DbType typ)
at System.Data.SQLite.SQLiteDataReader.GetDateTime(Int32 i)
at ConsoleHelpTicket.Data.FillQueue(SQLiteConnection conn, Queue`1 queue) in E:\...\Data.cs:line 142
Here's the property declaration:
public DateTime OpenDate { get; set; }
Here's the method where I try the assignment:
public static void FillQueue(SQLiteConnection conn, Queue<Ticket> queue)
{
try
{
var cmd = new SQLiteCommand($"select * from Tickets order by OpenDate desc;", conn);
using SQLiteDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Ticket ticket = new Ticket();
ticket.Tid = rdr.GetInt32(0);
ticket.Title = rdr.GetString(2);
ticket.Description = rdr.GetString(3);
ticket.OpenDate = rdr.GetString(5); <---------- PROBLEM LINE
ticket.ClosedDate = rdr.GetString(6);
ticket.Open = rdr.GetBoolean(7);
ticket.Location = rdr.GetString(8);
System.Console.WriteLine($"Added TID: {rdr.GetInt32(0)} Title: {rdr.GetString(2)} to queue.");
queue.Enqueue(ticket);
}
}

So I figured out the problem(s).
I was using SQLite's TIMESTAMP as the default column value, and I was allowing that to be set for the column value on every insert. I edited C# insert line to insert DateTime.Now. Yes SQLite supports TimeStamp as a data type, but the documentation says it's really stored as a string. See the documentation
After much troubleshooting I realized I got my column numbers mixed up and I was trying to import parse a bool as time, which obviously will throw an exception. I'm rewriting my code now to be more dynamic to avoid this type of problem in the future.
Finally I ended up using this line ticket.OpenDate = DateTime.Parse(rdr.GetString(6)); to successfully assign the column value to a DateTime property. My thinking is that if SQLite is storing it as a string, I should retrieve it as a string and parse it to DateTime. I'm also storing the value as a native DateTime value from C# instead of relying on the TimeStamp value from SQLite. I haven't tested to see if I can parse a TimeStamp value though.

Related

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.

data mismatch in criteria expression using Oledb, C#, Dapper

I am simply inserting data using [c#, DAPPER, MS Access] inside a table but the column which is [DateTime] in c# is mapped to [date/time] column in MsAccess cause a issue like "data mismatch in criteria expression".
After a long research I got a link, but the "Floor" solution doesn't work here. Any suggestions are most welcome.
The best solution I found is changing the parameter syntax inside the query string from #Foo to ?Foo?.
The reason for this is explained in https://github.com/StackExchange/Dapper/issues/911
Try something like this which works for me:
#CLStart = DateTime.Parse(Convert.ToDateTime(client.CLStart).ToShortDateString())
client.CLStart here is of data type DateTime? - a Nullable<DateTime>
I've had this issue before when handwriting SQL queries in combination with Dapper and MS Access when the query has multiple parameters. The problem is that the access engine doesn't respect parameter order and sorts them alphabetically.
Take for example this sample table named MyTable:
MyNumber Number
MyDate Date/Time
Assuming you had a C# class MyClass:
public class MyClass
{
public int MyNumber { get; set; }
public DateTime MyDate { get; set; }
}
And had an instance myClass that you passed into the following Dapper statement:
connection.Execute("INSERT INTO MyTable (MyNumber, MyDate) VALUES (#MyNumber, #MyDate)", myClass);
The statement would fail due to System.Data.OleDb.OleDbException: 'Data type mismatch in criteria expression.'
This is because Access will sort the parameters alphabetically, which in this case causes it to try to stick a number into a date and a date into a number.
You can workaround this by naming your parameters in an alphabetically-friendly manner, or ordering them alphabetically. In this case the above statement could be rewritten as:
connection.Execute("INSERT INTO MyTable (MyDate, MyNumber) VALUES (#MyDate, #MyNumber)", myClass);
I had the same problem in vb.net, and I solved it this way.
Public Shared Function dapperBugDate(ByVal birthday As Date)
birthday = birthday.ToString("yyyy-MM-dd HH:mm:ss")
Return birthday
End Function
Sub save()
Dim actions = db.Execute("INSERT into table (birthday) VALUE (#birthday)", New table With {.birthday = dapperBugDate(Me.birthday)})
End Sub
Converting the DateTime to string worked for me. For example,
using (var conn = new OleDbConnection(connectionString))
{
conn.Open();
string sql = "Insert into TableName (TableId, DateChanged) values (#TableId, #DateChanged);";
var effectedRowCount = await conn.ExecuteAsync(sql, new { TableId = 1, DateChanged = DateTime.Now.ToString()});
conn.Close()
}

Insert decimal value into Currency Access field

Short background: I am replacing an outdated, Visual Based based program with a C#-based one. With the program comes an Access (JET) based database which is already in use. For now, I want to connect to the current database for practical reasons. However in the foreseeable future I'd like to replace it with SQL Server.
Question: The database stores financial information using the Access 'Currency' datatype. In C#, I use 'Decimal' to represent financial information. Naturally, I get an error when I try an INSERT or UPDATE-query to store the Decimal in the Currency-field (actually, I get an error once and with it, it automatically changes the datatype of that specific record to Decimal). I'm not sure what the best way to solve this problem is:
Transform the Decimal into Currency in my program and inserting it after. Is this even possible (C# says the values from the field are of the DBNull-type since it doesn't know Currency)? If yes, how do I do this?
Change the datatype of the field into Decimal. Will I risk corrupting financial information when I do this?
Any other/better suggestions?
Thanks in advance!
The exact error message:
System.Data.Odbc.OdbcException (0x80131937): ERROR [07006]
[Microsoft][ODBC Microsoft Access-stuurprogramma]Inbreuk op kenmerk van beperkt gegevenstype
It's Dutch and translates to Restricted data type attribute violation
My UPDATE code:
public Boolean setFuelCosts(int rentID, Decimal fuelcosts)
{
string conString = lem2;
string queryString = "UPDATE rental SET fuel = ? WHERE rentid = ?";
OdbcCommand command = new OdbcCommand(queryString);
command.Parameters.Add("#fuel", OdbcType.Decimal).Value = fuelcosts;
command.Parameters.Add("#rentid", OdbcType.Int).Value = rentID;
return factory.executeUpdateCommand(conString, command);
}
public Boolean executeUpdateCommand(String conString, OdbcCommand command)
{
bool result = false;
using (OdbcConnection connection = new OdbcConnection(conString))
{
try
{
command.Connection = connection;
command.CommandType = CommandType.Text;
connection.Open();
int i = command.ExecuteNonQuery();
result = (i >= 1);
connection.Close();
}
catch (Exception exc)
{
System.Windows.Forms.MessageBox.Show(exc.Message);
System.Windows.Forms.MessageBox.Show(exc.StackTrace);
}
}
return result;
}
Your issue appears to be a limitation of the Access ODBC driver when dealing with Currency fields using System.Data.Odbc in .NET. An OdbcDataReader will return those fields as System.Decimal (if the value is not NULL), but System.Data.Odbc apparently won't accept a System.Decimal parameter for a Currency field.
As a workaround you could replace
command.Parameters.Add("#fuel", OdbcType.Decimal).Value = fuelcosts;
with
command.Parameters.Add("#fuel", OdbcType.NVarChar).Value = fuelcosts.ToString("0.0000");
I just tested this from C# 2010 against an Access 2000 database file and it worked for me.

How to store a sql result to a variable in C# using Dapper.NET

I'm using dapper in my project, a beautiful tool for storing the SQL query results to a List and this working good.
I wrote a SQL query to fetch a record from database and store the result in a variable, I tried using dapper but stuck up with an error. I've pasted the corresponding code.
Exception: Unable to cast object of type
'System.Collections.Generic.List`1[Dapper.SqlMapper+DapperRow]' to
type 'System.IConvertible'.
try
{
using(var connection = ...)
{
connection.Open();
const string masterSelectQuery = "SELECT Id as [fileId], FileName as [fileName], Frequency as [frequency], Scheduled_Time as scheduledTime FROM MASTER_TABLE";
masterTableList = connection.Query<MasterTableAttributes>(masterSelectQuery).ToList();//Working fine
const string lastProcessedTimeQuery = "SELECT TOP 1 file_transfered_time as [lastProcessedTime] FROM PROCESS_LOGS ORDER BY file_transfered_time DESC";
DateTime lastProcessedTime = Convert.ToDateTime(connection.Query(lastProcessedTimeQuery)); //Here it fails
}
}
To overcome this error I used SQLCommand as follows
using (command = new SqlCommand(lastProcessedTimeQuery, connection))
{
DateTime lastProcessedTime = (DateTime)command.ExecuteScalar();//working fine
}
I am making a mistake in using dapper, can anyone please guide me?
Thanks in advance.
connection.Query(lastProcessedTimeQuery) returns a sequence of rows, each individually dynamic. Assuming file_transfered_time is a datetime, you have two choices here:
DateTime lastProcessedTime = connection.Query<DateTime>(
lastProcessedTimeQuery).Single();
or, to show how non-generic Query works with a dynamic row:
DateTime lastProcessedTime = connection.Query(
lastProcessedTimeQuery).Single().lastProcessedTime;
If your PROCESS_LOGS table could ever be empty, you might prefer SingleOrDefault:
DateTime? lastProcessedTime = connection.Query<DateTime?>(
lastProcessedTimeQuery).SingleOrDefault();
or:
var row = connection.Query(lastProcessedTimeQuery).SingleOrDefault();
DateTime? lastProcessedTime = null;
if(row != null) lastProcessedTime = row.lastProcessedTime;

"Data type mismatch" when using date Parameters

I am trying to implement CoolStorage in my project, but am running into an issue when using date parameters. When I pass in null as the date parameter it adds the record successfully, but if I pass an actual date through I get an error when calling .Save() to write the new record to the database. I have got the (Activa) CoolStorage source code and have found the point where it fails, but can't figure how to fix it.
The code where it fails is as follows (specifically the dbCommand.ExecuteNonQuery() line):
internal int ExecuteNonQuery(string sqlQuery, CSParameterCollection parameters)
{
long logId = Log(sqlQuery, parameters);
IDbCommand dbCommand = CreateCommand(sqlQuery, parameters);
try
{
dbCommand.ExecuteNonQuery();
return 1;
}
catch (InvalidOperationException)
{
return -1;
}
finally
{
LogEnd(logId);
}
}
The sqlQuery contains:
insert into [Schedule] ([TaskID],[StartTime],[MondayYn],[TuesdayYn],[WednesdayYn],[ThursdayYn],[FridayYn],[SaturdayYn],[SundayYn],[DefaultSysuserID],[DefaultTeamID],[ActiveYn])
values (#P17,#P18,#P19,#P20,#P21,#P22,#P23,#P24,#P25,#P26,#P27,#P28)
#P18 contains the offending date, which is a CSParameter object with a value of:
{17/12/2012 18:52:44}
Also, the database being used is Access. The error that's thrown is a OldDbException which reads {"Data type mismatch in criteria expression."}.
Can anyone offer any advice on how to resolve this?
EDIT: The Schedule StartTime field is defined in the Schedule table as Date/Time. I need it to have its required propetry set to True, but have disabled in order to test adding records by leaving StartTime out. The parameters are being applied via the CoolStorage classes as follows:
Schedule schedule = Schedule.New();
schedule.TaskID = task.TaskID;
schedule.StartTime = DateTime.Now;
schedule.MondayYn = true;
schedule.TuesdayYn = true;
schedule.WednesdayYn = true;
schedule.ThursdayYn = true;
schedule.FridayYn = true;
schedule.SaturdayYn = false;
schedule.SundayYn = false;
schedule.DefaultSysuserID = sysuser.SysuserID;
schedule.DefaultTeamID = sysuser.SysuserTeams.First().TeamID;
schedule.ActiveYn = true;
schedule.Save();
When I comment out the Schedule.StartTime = DateTime.Now line I can successfully add records, otherwise I get the error described above. I cannot change the format to text without editing my ORM mapping class, which will no doubt cause errors elsewhere. I guess I could alter the CoolStorage DataProvider class, but I'm assuming that this shouldn't be necessary?
EDIT2: As a test I intercepted the SQL posted above to remove the #P18 reference and hard code the date in its place and the record added correctly:
insert into [Schedule] ([TaskID],[StartTime],[MondayYn],[TuesdayYn],[WednesdayYn],[ThursdayYn],[FridayYn],[SaturdayYn],[SundayYn],[DefaultSysuserID],[DefaultTeamID],[ActiveYn])
values (#P17,#2012-12-01 12:00:00#,#P19,#P20,#P21,#P22,#P23,#P24,#P25,#P26,#P27,#P28)
I also tried modifying the value of the parameter by casting it as a string formatted as #yyyy-MM-dd hh:mm:ss# however I still received the Data type mismatch in criteria expression error.
EDIT3: I have fixed it by following Abhishek's (edit - also Dean's) suggestion of converting the DateTime to a string by amending the CSParameterCollection class as follows. Hopefully this won't cause problems if I decide to use a different database but it's fixed it for Access:
public CSParameter this[string name]
{
get
{
CSParameter parameter;
_parameterMap.TryGetValue(name, out parameter);
if (parameter.Value.GetType().Equals(typeof(DateTime)))
{
DateTime date = (DateTime)parameter.Value;
string dateString = date.ToString("yyyy-MM-dd hh:mm:ss");
parameter.Value = dateString;
}
return parameter;
}
}
I strongly feel that the problem is because the parameters are not in the same order when you are adding them to the parameter collection.
Make sure that they are in the same order and this applies to both sql statements or stored procedures.
ADO.NET does not support named parameters when using an OLEDB provider, and since you are connecting to an Access DB, you are actually using an OLEDB provider. So the order of the parameters does matter.
If they are in order and it's still not working, then I think that it might be an issue with the DateTime. Try converting it to string before adding it as a parameter.

Categories