C# - InvalidCastException when fetching double from sqlite - c#

I keep getting a InvalidCastException when I'm fetching any double from my SQLite database in C#. The exception says "Specified cast is not valid."
I am able to see the value in a SQL manager so I know it exists. It is possible to fetch Strings (VARCHARS) and ints from the database. I'm also able to fetch the value as an object but then I get "66.0" when it's suppose to be "66,8558604947586" (latitude coordination).
Any one who knows how to solve this?
My code:
using System.Data.SQLite;
...
SQLiteConnection conn = new SQLiteConnection(#"Data Source=C:\\database.sqlite; Version=3;");
conn.Open();
SQLiteDataReader reader = getReader(conn, "SELECT * FROM table");
//These are working
String name = reader.GetString(1);
Int32 value = reader.GetInt32(2);
//This is not working
Double latitude = reader.getDouble(3);
//This gives me wrong value
Object o = reader[3]; //or reader["latitude"] or reader.getValue(3)

You may want to check the data type you used to store the value in the database. It seems like you may be storing an integer but trying to retrieve it as a double.

Is the actual table schema defined to use a DOUBLE? SQLite will do some translation since the DB engine is typeless. If it is actually a SQL DOUBLE column, you could try a cast as well.
More information on SQLite type affinity.

Related

Reading results of SP with OdbcDataReader gives Error 22018 - Error in assignment

Ok I don't get this. I haven't used ODBC classes before but figuered it's nothing special for basic use. And it does work except in this case.
I need to execute stored procedure without parameters via ODBC connection and get the results, parse the rows into objects and insert them in my local DB. And it worked with test data but now fails with live data, while customer is able to execute the same PS via some other tool... The real trouble is that I have to run it on live server, so I can't debug, instead I created small project which writes output into TextBox. Anyway, here's the code:
var ODBCConnection = new OdbcConnection();
ODBCConnection.ConnectionString = "something...";
//using command "exec schema.spName" or "exec schema.spName()" or "{ call schema.spName()}" runs the procedure
//putting only name "schema.spName" gives ERROR [42000]
var cmd = new OdbcCommand("exec schema.spName())", ODBCConnection);
cmd.CommandType = CommandType.StoredProcedure;
DbReader = cmd.ExecuteReader();
int fCount = DbReader.FieldCount;
infoBox1.Text += System.Environment.NewLine + "Results:";
for (int i = 0; i < fCount; i++)
{
String fName = DbReader.GetName(i);
infoBox1.Text += fName + "|";
}
This list all the column names in result and there are 20 columns.
while (DbReader.Read())
{
var row = new RowClass();
for (int i = 0; i < fCount; i++)
{
object val = DbReader.GetValue(i);
//check which column this is and parse it to set properties of RowClass
//Expected values are string, int and decimal
}
}
This works for the first 10 rows but breaks when it tries to read for following columns with error:
ERROR [22018] [Cache ODBC][State : 22005][Native Code 22005]
[path to .exe]
Error in assignment
No StackTrace no InnerException.
I tried skipping 11th column because it started there, but breaks for every column after the first 10.
I am clueless... if it read there are 20 fields then wtf... null values are not problem because it returns DBNull, it works on other places (not executing SP but doing select queries).
Client executed sp connectin from same net environment and send me picshot, and csv of data. Nothing strane in data itself.
Anyone had this before? Should I use something else for instead of OdbcDataReader?
Thank you.
OK, I managed to figure it out thanks to customer admin who was executing the same procedure by some browser SQL tool. There I saw that 11th column was of type Date. ODBC returned type INT for that column. So I had to call another admin who set up ODBC and he changed column type to varchar so now it finally works.
I guess I can now assume how DataReader works if one wrong column type made all subsequent columns impossible to read as well.
I don't feel like accepting my own answer, so if someone would be kind enough to give me few links about how ODBC works and how DataReader works, not just how to use it, and maybe can quote some text that explains this behavior for this kind of mistake...

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.

tinyint in SQL Server to byte in C#

In database management and application development we must be mindful of space and memory requirements. I have always been taught to use the data type that uses the least amount of space for your needs.
In my scenario, I have a column in a table that stores values {0,1,2,3,4} (SQL Server 2012). For this I chose to use the tinyint datatype. These values are pulled from the database into a C# application. Currently, I cannot get this tinyint data type to convert to a byte. When I try, I get an error "Cannot implicitly convert int to byte". If I change the datatype in the application to an integer, I can pull it just fine. Same with a string.
For performance purposes, is it okay to use integers throughout my entire application where I would normally use byte? If not, how do you convert an integer to a byte?
This is the code that I use that gives an error:
string strSQL = "SELECT securityLevel FROM security WHERE employeeID=#empID;";
using (SqlConnection dbConn = new SqlConnection(connParam))
{
dbConn.Open();
byte level = 0;
using (SqlCommand dbCommand = new SqlCommand(strSQL, dbConn))
{
dbCommand.CommandType = System.Data.CommandType.Text;
dbCommand.Parameters.AddWithValue("#empID", "12345");
using (SqlDataReader dbReader = dbCommand.ExecuteReader())
{
while (dbReader.Read())
{
level = dbReader.GetByte(0);
}
}
}
Console.WriteLine(level);
Console.ReadLine();
}
I have also tried:
level = (byte)dbReader.GetValue(0);
Yes, you were correct to pick TINYINT as the datatype if you are storing only 0 - 4.
Yes, TINYINT equates to a byte in .Net. You can see a list of mappings here:
http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
No, you did not actually use TINYINT when creating the table, else you would not be getting this error. The error message is very specific about the source datatype being INT.
No, do not use INT to store these values. That is unnecessary and what you are attempting to do (i.e. TINYINT and byte) is perfectly valid and I have done it many times.
Assuming you do not have millions of rows of data in that table and constant hits against it, run the following:
ALTER TABLE [security] ALTER COLUMN [securityLevel] TINYINT NOT NULL;
(I am assuming that the column is currently NOT NULL and in that case, if you leave off the NOT NULL in the ALTER TABLE statement, it will change the field to TINYINT NULL. If the field isn't currently NOT NULL, then just leave that part off)
I wrote the following LinqPad (against SqlServer express) to verify that you can read in a tinyint as a byte using .NET and SqlServer:
var cb = new SqlConnectionStringBuilder { DataSource = #".\Sqlexpress", InitialCatalog = "Medallion_OData_Tests_CustomersContext251990930203", IntegratedSecurity = true };
using (var c = new SqlConnection(cb.ConnectionString))
{
c.Open();
var cmd = c.CreateCommand();
cmd.CommandText = "SELECT CAST(1 AS tinyint)";
var reader = cmd.ExecuteReader();
reader.Read();
reader.GetByte(0).Dump();
}
This suggests to me that the actual securityLevel column in your database is not of the type TINYINT. To verify, why not temporarily modify your select query to include a CAST to TINYINT as in my example? If this works, that will confirm that the table schema is the problem.
Other ways to check the actual table schema include querying sys.columns or highlighting the name of the table in SqlServer Management Studio and hitting ALT+F1.
I think safest way is to use Convert.ToByte Method:
level = Convert.ToByte(dbReader.GetValue(0));
It converts from many value types to byte.

Oracle sql setting integer

I set my table column as integer.
Now I am trying to read it in my c# code using getint32 and for some reason I get a cast error, and when I checked some more I saw that I am getting a decimal from my db. how can that be? Isn't the oracle integer equals to c# int?
using (OracleCommand cmd = new OracleCommand(#"select id,title from table"))
{
cmd.Connection = _conn;
OracleDataReader r = cmd.ExecuteReader();
while (r.Read())
{
Debug.WriteLine(reader.GetFieldType(0)); // <--decimal
//reader.GetDecimal(0);
reader.GetInt32(0); <---cast error
Debugger.Break();
}
r.Close();
}
the id column is set as integer, also tryed number. comfused :S
Have a read at this:
Which .NET data type is best for mapping the NUMBER Oracle data type in NHibernate?
Oracle number maps to .net decimal. Microsoft is aware of this issue.
You shouldn't do that.
An int (System.Int32) is not big enough to hold every possible decimal value. If your column type is decimal, use GetDecimal() method, if your column type is int, use GetInt32() method.
There is no implicitly conversation decimal to int at all.

How to insert string in CLOB type column in oracle db using c#.net

i am trying to save a xml file as a string in clob type column in oracle db from c# am not sure how to insert clob type data from c#.
code here:
public bool Insert_XMLDocument(string ReportType,object XMLDocument)
{
try
{
Database db = DatabaseFactory.CreateDatabase("XMLDOC_ConnectionString");
DbCommand dbc = db.GetStoredProcCommand("insert_XMLDOC");
dbc.CommandType = CommandType.StoredProcedure;
db.AddInParameter(dbc, "pid", DbType.Int32, 1);
db.AddInParameter(dbc, "repo_document", DbType.Object,XMLDocument);
int i = db.ExecuteNonQuery(dbc);
if (i > 0)
return true;
else
return false;
}
catch (Exception ex) {
//HandleException(ex);
return false; }
}
Error due to compilation of this : Cannot bind type System.String as Blob.
Can you show us your stored procedure and/or your db.AddInParameter() method? Without seeing more code this is more or less just a guess:
It seems like passing DbType.Object to your data layer would indicate that the data type of the parameter is supposed to be a BLOB (binary) yet you have indicated you want it to be a CLOB (character).
Let's assume your stored procedure is defined like this:
CREATE PROCEDURE insert_XMLDOC (pid IN NUMBER, repo_document IN CLOB)
If the input object XMLDocument parameter is a string, or if your db.AddInParameter() method is converting this object to a string or any sort of textual representation then your data layer could be trying to assign a string as a BLOB parameter.
instead of using database factory i used the .net oracle provider method in which we can get "OracleType.Clob" which solves the problem i just passed the xml document as string and the job was done
Although the question seems to be outdated, I want to share an example that worked for me.
My intention was to save a JSON string (with more than 32k characters) into a clob field.
This is what I did:
string JSON_string = JsonConvert.SerializeObject(SomeObject);
System.Data.OleDb.OleDbCommand myCommand = new System.Data.OleDb.OleDbCommand();
myCommand.Parameters.AddWithValue("", SomeAttribute);
myCommand.Parameters.AddWithValue("", SomeAttribute2);
myCommand.Parameters.AddWithValue("", SomeAttribute3);
myCommand.Parameters.AddWithValue("", JSON_string);
And then execute the command. I'm using our companies library to do that, so I don't have to worry about the database connection:
DataSet myDS = myUser.myLoginUser._MySpAppS.RunSQL("INSERT INTO MARS$T_BCSAVINGS (MASSNAHMEN_ID, USER_ID, AKTIV, HEBELDATEI) VALUES (?, ?, ?, ?);", myCommand.Parameters);
I'm saving the result in a DataSet only to check if the query was successful.
So basically what I did, is to handover the string to the OleDbCommand parameters list and executed the query with those parameters.

Categories