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

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()
}

Related

Convert SQLite timestamp to DateTime in 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.

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.

Get the "sql datatype" of a column created in SQL server using C# and entity framework

I have a table tblDetails consisting of 2 columns
Id INT NOT NULL
Name VARCHAR(20)
By using EntityFramework 6 and C# for my WPF application im fetching the data to display it in the front end. Also I want to get the "sql datatype" of the columns as well.
This is the entity model used for database connectivity:
public class tblDetails
{
public int Id{get; set;}
public string Name{get;set;}
}
For this I'm using the GetProperty() method for it.
Below is a sample of code:
private void GetDataType()
{
var col = typeof(tblDetails).GetProperty("Name");
string dataType = col.PropertyType.Name;
}
PROBLEM
dataType varibale is assigned with the value string but I want the exact Sql datatype of the column i.e. VARCHAR(20)
I've gone through a number of solutions like below but didn't get the solution to my problem.
How can I get data type of each column in a SQL Server table/view etc. using C# Entity Framework?
Anybody got a C# function that maps the SQL datatype of a column to its CLR equivalent?
Entity Framework Data Annotations Set StringLength VarChar
UPDATE
Below is the class in which I'm using the Dbcontext:
public class DomainService : DbContext
{
public DomainService() : base("ConnectionString")
{
Configuration.LazyLoadingEnabled = true;
#if DEBUG
Database.Log = (s) => Debug.WriteLine(s);
#endif
}
public DbSet<tblDetails> tblDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Entity Framework (and actually any "generic ORM") are meant to be generic, and therefor you somehow lost the ability to know the exact datatype.
Even if you're looking at the attributes or mapping that sometimes are stored by different ORM's to enable some kind of validations prior to actually doing the work against the DB - you can't guarantee someone didn't change the schema on the SQL Server end - and didn't update the model.
In most cases it shouldn't really matter - working with the matching CLR data-types should give you enough info (for example, for user interface usage).
If you need to know the exact data type, as stored and known by the server, you have the following options:
If you're interested in table or view schema, you can use SMO (SQL Server Management object) to connect to the server and query the actual schema of the table or view.
If you want to know the schema returned by specific query, you can use sp_describe_first_result_set SP (https://msdn.microsoft.com/en-us/library/ff878602.aspx) that gets SQL query and return result-set describing each column you can expect in the result set of the query. You can probably run that once for the queries you use, and then cache the results or something.
Hope it helps.
Got a solution:
To get the datatype and length as well of variables of type VARCHAR, NCHAR & NVARCHAR
SqlConnection connection = new SqlConnection("ConnectionString");
connection.Open();
string sqlcmnd = string.Format(
#"SELECT CASE UPPER(DATA_Type)
WHEN 'NCHAR' THEN CONCAT(UPPER(DATA_Type),'(',ISNULL(CHARACTER_MAXIMUM_LENGTH,''),')')
WHEN 'VARCHAR' THEN CONCAT(UPPER(DATA_Type),'(',ISNULL(CHARACTER_MAXIMUM_LENGTH,''),')')
WHEN 'NVARCHAR' THEN CONCAT(UPPER(DATA_Type),'(',ISNULL(CHARACTER_MAXIMUM_LENGTH,''),')')
ELSE UPPER(DATA_Type) END AS DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{0}' AND COLUMN_NAME = '{1}'", tableName, ColumnName);
SqlCommand cmd = new SqlCommand(sqlcmnd, connection);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
string dataType = reader["DATA_TYPE"].ToString();
Use DbContext object instead your own created model class in reflection
DomainService context = new DomainService();
private void GetDataType()
{
var col = typeof(context.tblDetails).GetProperty("Name");
string dataType = col.PropertyType.Name;
}

Error when updating date field in sql server using c#

I am trying to insert a date (date only, not datetime) into sql table (the datatype is date).
I am using the '23/07/2013' format which I am getting from jquery datepicker.
When I execute the following sql, I am getting following error.
SQL: UPDATE qmsAuditFindings SET FindDate='23/07/2013' WHERE AuditID=37
Please advise.
Also its worth mentioning that the insert statement with the exact format works just fine. Just the update that does not.
At the surface, this is simply a formatting issue - but "fixing the formatting" is the wrong way to address this; you should parameterize, such that formatting simply does not apply. Dates don't have a "format", as such - they are just numbers. For example, what we should probably execute is:
UPDATE qmsAuditFindings SET FindDate=#findDate WHERE AuditID=#auditId
To do that you get the DateTime value in your .NET code, and do something like:
DateTime findDate = ...
int auditId = ...
using(var cmd = connection.CreateCommand()) {
cmd.CommandText =
"UPDATE qmsAuditFindings SET FindDate=#findDate WHERE AuditID=#auditId";
cmd.Parameters.AddWithValue("findDate", findDate);
cmd.Parameters.AddWithValue("auditId", auditId);
cmd.ExecuteNonQuery();
}
or more simply with a tool like "dapper":
DateTime findDate = ...
int auditId = ...
connection.Execute(
"UPDATE qmsAuditFindings SET FindDate=#findDate WHERE AuditID=#auditId",
new { findDate, auditId });

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;

Categories