Why my Access DB rejects an insert date time? - c#

I have this table created on an Access DB.
string sql = $"CREATE TABLE Eventi (" +
$"id AUTOINCREMENT PRIMARY KEY," +
$"tipologia varchar(255)," +
$"denominazione varchar(255)," +
$"descrizione varchar(255)," +
$"data date," +
$"costo decimal," +
$"prezzo decimal" +
$");";
I use this query to insert new records in the table, but when i exec the query it returns me a 'System.Data.OleDb.OleDbException: 'Data type mismatch in criteria expression.'.
cmd.CommandText = "INSERT INTO Eventi (tipologia,denominazione,descrizione,data,costo,prezzo) VALUES " +
"(#tipologia, #denominazione, #descrizione, #data, #costo, #prezzo);";
Here it is the code to add the querie's parameters:
cmd.Parameters.Add("#id", OleDbType.Integer).Value = v.Id;
cmd.Parameters.Add("#tipologia", OleDbType.VarChar, 255).Value = v.Tipologia;
cmd.Parameters.Add("#denominazione", OleDbType.VarChar, 255).Value = v.Denominazione;
cmd.Parameters.Add("#descrizione", OleDbType.VarChar, 255).Value = v.Descrizione;
cmd.Parameters.Add("#data", OleDbType.Date).Value = v.Data.Date;
cmd.Parameters.Add("#costo", OleDbType.Double).Value = v.Costo;
cmd.Parameters.Add("#prezzo", OleDbType.Double).Value = v.Prezzo;

The real problem in your query is not the type difference between decimal and double. While it is really good practice to always express the correct datatype, in this context, the parameters of type double can be easily converted to decimal if the properties Costo and Prezzo are of type decimal.
The real problem is the presence of a first parameter named #ID while you don't have this field in your fields names and in your parameters placeholders.
In OleDb parameters are not recognized by their name and assigned to their placeholders using the name property. (Indeed the documentation tells us to name these placeholders all with the ? character). In OleDb parameters are positional, so the first parameter will be used as the value for the first placeholder. In your code this results in a shift of every parameter by one position leading to the parameter #Descrizione assigned to the field data and of course that string is not a date.
Just remove the first parameter from your collection and everything should be fine.
Side note. In Access, as well in MySql, you can assign a value to an AUTOINCREMENT/AUTONUMBER field if you really want. SqlServer instead doesn't allow this practice without turning off the IDENTITY property of the column.

ID value is not needed because is an autoincrement field.
As Liam point out in comments you need to use OleDbType.Decimal instead of OleDbType.Decimal
Or you can also do not declare the type (it will be decided automatic)
cmd.Parameters.Add(new SqlParameter("#CODE", myCode));

Related

Passing in Oracle Parameter to SQL string

I'm having a problem where I don't know how I'm supposed to pass in an Oracle parameter where the C# type is a string and the Oracle type is a Varchar2.
Currently I'm passing in this string as CMS','ABC thinking that Oracle will add in the '' that surround this string making it a varchar2 that looks like 'CMS','ABC'.
This works for a single string like CMS but when the value is something longer, like something typically in a IN (list) command the parameter won't be passed in correctly.
This is the code I'm referring too.
string sql = 'SELECT name FROM Pers p WHERE p.FirstName IN (:names)';
The below works when the value of :names being passed in is CML without any quotes.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML", ParameterDirection.Input);
Below doesn't work when the value of :names being passed in is CML','ABC with quotes on the inside.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML','ABC", ParameterDirection.Input);
Why is that?
Does Oracle add in single quotes around the parameter when it's passed into the sql statement? Why doesn't it add quotes around the second case?
ODP.NET parameters do not work with multiple, comma separated values. Each parameter is treated as a single value, whatever kind of quotes it contains.
Oracle does not add quotes around parameter values when passed to a query. Quotes are just a way to write a VARCHAR value in a query, but when using parameters, Oracle doesn't "replace your parameter with its value then execute the query", as this would allow SQL injection.
If that was the case, imagine your parameter value was: "CML', 'ABC');DROP DATABASE Test;--". Oracle would then execute SELECT name FROM Pers p WHERE p.FirstName IN ('CML', 'ABC');DROP DATABASE Test;--'!
See this question for ideas on how to solve your problem: Oracle Parameters with IN statement?
From your comments/answers I was able to come up with this solution. I hope it helps others who come.
To get around ODT.NET parameters not working with multiple comma separated values you can divide each value into its own parameter. Like the following.
string allParams = "CML, ABC, DEF";
string formattedParams = allParams.Replace(" ", string.Empty); // Or a custom format
string [] splitParams = formattedParams.Split(',');
List<OracleParamter> parameters = new List<OracleParameter>();
string sql = #"SELECT * FROM FooTable WHERE FooValue IN (";
for(int i = 0; i < splitParams.Length; i++)
{
sql += #":FooParam" + i + ",";
parameters.Add(new OracleParameter(":FooParam" + i, OracleDbType.Varchar2, splitParams[i], ParameterDirection.Input));
{
sql = sql.Substring(0, (sql.Length - 1));
sql += ')';
The string sql will now have this as it's value: SELECT * FROM FooTable WHERE FooValue IN (:FooParam0,:fooParam1, etc...)
This will solve the problem.
Another approach would be to add in a bunch of OR clauses for each parameter. The above example is better since you don't write a bunch of OR clauses though.

"Data type mismatch in criteria expression." ACCESS 2010 and C#

I'm trying to make a query in Access 2010, but it keeps giving me this error "Data type mismatch in criteria expression." over the line "dataCommand.ExecuteNonQuery();"
String sql1 = "INSERT INTO Formulario (Numero, Localidad, [Parroquia o Institucion], Fecha, [Id Institucion], Comunitaria, Observacion) VALUES (#Numero, #Localidad, [#Parroquia o Institucion], #Fecha, [#Id Institucion], #Comunitaria, #Observacion)";
mi_conexion.Open();
//insertar tabla formulario...........
dataCommand.CommandText = sql1;
dataCommand.Parameters.Add("#Numero", OleDbType.VarChar).Value = numero;
dataCommand.Parameters.Add("#Localidad", OleDbType.VarChar).Value = textBox_localidad.Text;
dataCommand.Parameters.Add("#Parroquia o Institucion", OleDbType.VarChar).Value = comboBox_instituciones.Text;
dataCommand.Parameters.AddWithValue("#Fecha", "#" + fecha.Value.Date.Day.ToString() + "/" + fecha.Value.Date.Month.ToString() + "/" + fecha.Value.Date.Year.ToString() + "#");
dataCommand.Parameters.Add("#Id Institucion", OleDbType.VarChar).Value = Int32.Parse(label_id2.Text);
dataCommand.Parameters.Add("#Observacion", OleDbType.VarChar).Value = obs;
if(radioButton_comunitaria.Checked)
{
dataCommand.Parameters.Add("#Comunitaria", OleDbType.VarChar).Value = true;
}
else
{
dataCommand.Parameters.Add("#Comunitaria", OleDbType.VarChar).Value = false;
}
dataCommand.ExecuteNonQuery();
I dont know what could be the reason.....
I tried this:
String sql1 = "INSERT INTO Formulario (Numero, Localidad, ParroquiaoInstitucion, Fecha, IdInstitucion, Comunitaria, Observacion) VALUES (?,?,?,?,?,?,?)";
mi_conexion.Open();
//insertar tabla formulario...........
dataCommand.CommandText = sql1;
dataCommand.Parameters.AddWithValue("Numero", numero);
dataCommand.Parameters.AddWithValue("Localidad", textBox_localidad.Text);
dataCommand.Parameters.AddWithValue("ParroquiaoInstitucion", comboBox_instituciones.Text);
dataCommand.Parameters.AddWithValue("Fecha", "#" + fecha.Value.Date.Day.ToString() + "/" + fecha.Value.Date.Month.ToString() + "/" + fecha.Value.Date.Year.ToString() + "#");
dataCommand.Parameters.AddWithValue("IdInstitucion", Int32.Parse(label_id2.Text));
if(radioButton_comunitaria.Checked)
{
dataCommand.Parameters.AddWithValue("Comunitaria", true);
}
else
{
dataCommand.Parameters.AddWithValue("Comunitaria", false);
}
dataCommand.Parameters.AddWithValue("Observacion", obs);
dataCommand.ExecuteNonQuery();
But it doesn't work yet -_-.....
Use AddWithValue instead of Add method. Sql Server will decide what type is the data for you ! I'm giving you an example. Also OLE DB does not support named parameters, only unnamed parameter
dataCommand.Parameters.AddWithValue("?", true);
You should set the parameters in the right order. Here is the source how to add parameters for OleDb in MSDN
Access database (even when OleDb is in action) does not know parameters which names start with #.
To be able to use parameters with MS Access database engine, use:
PARAMETERS [parameter1] type, [parameter2] type, [parameterN] type;
SELECT <FieldList>
FROM <TableName>
WHERE FieldName = [parameter1] AND ...
For further information, please see: PARAMETERS Declaration (Microsoft Access SQL)
Second way is to use non-named parameters:
SELECT <FieldList>
FROM <TableName>
WHERE FieldName = ? AND FieldName2 =?
But i prefer to use named parameters.
OleDb wants that you add your parameters in the same order in which their placeholder appears in the command text. OleDb using Ms-Access accepts also parameters with name that starts with # without problem (probably to ease the upsizing to Sql Server), but you should absolutely respect the order.
In your code you add the parameter for the field Comunitaria after the parameter for the field Observacion This creates the mismatch problem because OleDb passes the value for Comunitaria to the field Observacion and viceversa. The field Comunitaria then receives a value that cannot be converted to a boolean (Yes/No) value.
Try to change your code in this way
if(radioButton_comunitaria.Checked)
{
dataCommand.Parameters.Add("#Comunitaria", OleDbType.VarChar).Value = true;
}
else
{
dataCommand.Parameters.Add("#Comunitaria", OleDbType.VarChar).Value = false;
}
dataCommand.Parameters.Add("#Observacion", OleDbType.VarChar).Value = obs;
dataCommand.ExecuteNonQuery();
A part from this you should remove that spaces inside the parameters names. It could only give you troubles and it is not needed.
I suggest also to use a correct datatype for the Fecha field. If Access expects a Date don't try to build a replacement string like you are doing the insert inside the Access interface.
dataCommand.Parameters.AddWithValue("#Fecha", new DateTime(fecha.Value.Date.Year,
fecha.Value.Date.Month,
fecha.Value.Date.Day)

Getting C# To Insert Defaults Into SQL Server

How can I INSERT values into SQL Server that are stored in a string[] such that some of the values should be disregarded in favor of the values stored in SQL Server as default constraints on the table? What do I need to pass(e.g. NULL or something else) to use those defaults?
Currently, I add all the defaults in my code, and it is getting bulky.
Below is my code:
if (value == "") value = "0";
string insert = "INSERT INTO " + table + " (" + columns + ") VALUES (" + atColumns + ")";
using (SqlConnection connect = new SqlConnection(connection))
{
using (SqlCommand command = new SqlCommand(insert, connect))
{
//adds values to corresponding parameters
for (int i = 0; i < table_cols.Length; i++)
{
command.Parameters.AddWithValue("#" + table_cols[i], table_vals[i]);
}
foreach (SqlParameter Parameter in command.Parameters)
{
if (Convert.ToString(Parameter.Value) == "")
{
Parameter.Value = DBNull.Value;
}
}
connect.Open();
command.ExecuteNonQuery();
response = "Success";
return response;
If a specific Parameter.Value is not-null and has a default set by SQL Server, this code will not work with null.
In SQL Server, this gets handled by omitting the value in your insert statement (this omission triggers inserting the default value for the table, whereas providing null produces errors).
If you want SQL Server to use the default value constraint for the column then don't include the column as part of the insert parameters.
Example:
--From SQL Server
CREATE TABLE Orders
(
Id INT IDENTITY PRIMARY KEY
,Amount INT NOT NULL
,Cost MONEY NOT NULL
,SaleDate DATE NOT NULL DEFAULT GETUTCDATE()
);
//From C#
public int Insert(decimal cost, int amount)
{
using (var connection = new SqlConnection(connectionString))
{
var command = connection.CreateCommand();
//Don't specify the SaleDate and it will insert the current time
command.CommandText = "INSERT INTO Orders(Amount, Cost) VALUES(#Amount, #Cost); SELECT SCOPE_IDENTITY();";
command.Parameters.AddWithValue("#Amount", amount);
command.Parameters.AddWithValue("#Cost", cost);
using(var reader = command.ExecuteReader())
{
if(reader.Read())
return Convert.ToInt32(reader[0]);
}
}
return 0;
}
If you want to use a parameters list in your C# code then just keep the parameter names grouped with their values and if the value is null and it has a default value then just skip it.
Passing in a NULL tells SQL that you want a NULL in that column overriding the default. If you want to pass something in pass in the keyword DEFAULT. I wrote an article, "keyword DEFAULT", about the usage:
The DEFAULT keyword causes the default value (from the constraint) to be inserted into the column.
Just remember that when you pass in DEFAULT don't put quotes around it. That makes it the string DEFAULT rather than the keyword DEFAULT.
The only other way of doing it I can think of would be triggers based your approach (and you're better off coding it at that point).
However, if you alter your approach to use stored procedures, you can do your value handling natively in SQL, otherwise you're stuck coding it into your app... might i recommend Ternary statements for your example: http://msdn.microsoft.com/en-us/library/ty67wk28%28v=vs.80%29.aspx .
If you include a column in the column list, it will try and insert the value you give, it. It will not assume that NULL means "just insert the default value".
If you don't want to insert a value into that column, don't include it in your column list (or value list, obviously).
While it may seem more efficient to loop through the table columns and be agnostic of the column name, type, etc. In the long run you may be better off handling each column explicitly so you can choose whether or not to use a default, verify the value, etc.
I actually used the INFORMATION_SCHEMA.COLUMNS table to pull back from SQL Server all of the Column Defaults. Then I just organized the defaults into a string[] and looped through it to insert defaults rather than nulls (some defaults are null).

Exception using DateTime in a parameterized OleDbCommand

I'm trying to insert a System.DateTime into an Access database using a parameterized OleDbCommand in C#. However, it throws a Data type mismatch in criteria expression exception.
Here is my code:
string statement = "INSERT INTO Log (SCTID, LogDateTime, Type, Message, Visible)" +
"VALUES (?, ?, ?, ?, ?);";
OleDbCommand insertCommand = new OleDbCommand(statement, connection);
// Add parameters to the command
insertCommand.Parameters.AddWithValue("#SCTID", OleDbType.Integer).Value = SCTID;
insertCommand.Parameters.AddWithValue("#LogDateTime", OleDbType.DBTime).Value = dateTime;
insertCommand.Parameters.AddWithValue("#Type", OleDbType.Integer).Value = (int)logType;
insertCommand.Parameters.AddWithValue("#Message", OleDbType.BSTR).Value = message;
insertCommand.Parameters.AddWithValue("#Visible", OleDbType.Boolean).Value = visible;
insertCommand.ExecuteNonQuery();
When I comment out the LogDateTime line, the rest of it works. My problem is that no matter what I use for the DateTime type, it doesn't work. I've tried:
OleDbType.Date, OleDbType.DBDate, OleDBType.DBTimeStamp, DbType.Date, DbType.DateTime, DbType.DateTime2
I've also tried:
insertCommand.Parameters.AddWithValue("#LogDateTime", dateTime);
It doesn't work either. Nothing I've read through Google or SO works. Also, note that I do need both date and time, not just a date alone.
insertCommand.Parameters.AddWithValue("#SCTID", OleDbType.Integer).Value = SCTID;
...
That's a very strange way to use AddWithValue. Its second parameter is not the type - it's the value that you want it to have. As given, you just end up using the integral value of enumeration member OleDbType.Integer, and then immediately overwrite it by assigning to Value property. It should be either:
insertCommand.Parameters.AddWithValue("#SCTID", SCTID);
or:
insertCommand.Parameters.Add("#SCTID", OleDbType.Integer).Value = SCTID;
Regarding the statement itself - why do you use ? for placeholders in command text, but then use names when adding parameters to the collection?
Regarding the actual problem - looks like it's a known bug, and the workaround is to truncate milliseconds in your DateTime value before assigning, and to use OleDbType.Date.

i'm lost: what is wrong with this ado.net code?

well, the question is clear i hope, the code is this:
string sql = "delete from #tabelnaam";
SqlCommand sc = new SqlCommand();
sc.Connection = getConnection();
sc.CommandType = CommandType.Text;
sc.CommandText = sql;
SqlParameter param = new SqlParameter();
param.Direction = ParameterDirection.Input;
param.ParameterName = "#tabelnaam";
param.Value = tableName;
sc.Parameters.Add(param);
OpenConnection(sc);
sc.ExecuteScalar();
tableName is supplied to this function.
I get the exception:
Must declare the table variable #tabelnaam
IIRC, you cant use a substitute the table name for a parameter.
Rather build the SQL string containing the correct table name.
Make to changes
rather than using paramter use this
string sql = string.format( "delete from {0}",tableName);
make use of executenonquery intead of ExecuteScalar
sc.ExecuteNonQuery();
As mentioned by others, you can't parameterise the table name.
However, as you rightly mention in comments on other answers, using simple string manipulation potentialy introduces a SQL injection risk:
If your table name input is fro an untrusted source, such as user input, then using this:
string sql = string.format( "DELETE FROM {0}",tableName);
leaves you open to the table name "myTable; DROP DATABASE MyDb" being inserted, to give you:
DELETE FROM myDb; DROP DATABASE MyDB
The way round this is to delimit the table name doing something such as this:
string sql = string.format("DELETE FROM dbo.[{0}]", tableName);
in combination with checking that the input does not contain either '[' or ']'; you should probably check it also doesn't contain any other characters that can't be used as a table name, such as period and quotes.
I dont think you can parameterize the table name. From what I have read you can do it via Dynamic sql and calling sp_ExecuteSQL.
Your SQL is incorrect, you are deleting from a table variable yet you haven't defined that variable.
Update: as someone has pointed out, you are trying to dynamically build a query string but have inadvertantly used SQL parameters (these do not act as place holders for string literals).
More here:
Parameterise table name in .NET/SQL?
You cannot parameterise the table name, you have to inject it into the command text.
What you can and should do is protect yourself against SQL injection by delimiting the name thus:
public static string Delimit(string name) {
return "[" + name.Replace("]", "]]") + "]";
}
// Construct the command...
sc.CommandType = CommandType.Text;
sc.CommandText = "delete from " + Delimit(tableName);
sc.ExecuteNonQuery();
See here and here for more background info.

Categories