Oracle sql setting integer - c#

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.

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.

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.

How can the same decimal have different bits and be formatted differently by ToString()?

In our Oracle database, I have a table (say MYTABLE) with column VALUE_NUMBER of type NUMBER (NULL, no precision specified). This table contains a value 1178.2
When retrieving data from this column using standard ADO.Net (actually ODP.Net) and then converting the decimal value to a string, I get '1178.20'. Obviously, when converting the constant 1178.2M to a string, the output is 1178.2.
Digging deeper, I looked at the output of decimal.GetBits(), which turned out to be different, although when comparing both numbers they are really considered equal.
The code sample below illustrates the behaviour:
using (var connection = new OracleConnection("my connection string"))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT VALUE_NUMBER FROM MYTABLE";
command.CommandType = CommandType.Text;
using (command)
{
var reader = command.ExecuteReader(CommandBehavior.Default);
reader.Read();
decimal oracleDecimal = reader.GetDecimal(reader.GetOrdinal("VALUE_NUMBER"));
Console.WriteLine(oracleDecimal); // output: 1178.20 (NOT expected)
var bitsFromOracle = decimal.GetBits(oracleDecimal).Select(x => x.ToString());
var bitsFromOracleString = string.Join(",", bitsFromOracle.ToArray());
Console.WriteLine(bitsFromOracleString); // 117820,0,0,131072
// sanity check
const decimal constantDecimal = 1178.2M;
Console.WriteLine(constantDecimal); // output: 1178.2 (expected)
var bitsFromConstant = decimal.GetBits(constantDecimal).Select(x => x.ToString());
var bitsFromConstantString = string.Join(",", bitsFromConstant.ToArray());
Console.WriteLine(bitsFromConstantString); // 11782,0,0,65536
Console.WriteLine(oracleDecimal == constantDecimal); // True
}
}
How should this be explained?
Here's the table CREATE & INSERT script to make this work:
CREATE TABLE MYTABLE (
ID NUMBER(10) NOT NULL,
VALUE_NUMBER NUMBER
);
INSERT INTO MYTABLE(ID,VALUE_NUMBER) VALUES(1,1178.2);
UPDATE:
The answer by #Vash and his subsequent comment led to the right conclusion that the .Net decimal type actually contains information about it's number of digits, even if they are irrelevant for equality. 1178.2M and 1178.20M have a different bit representation because of this, although obviously the .Equals() method and == operator consider these numbers equal.
The main problem you have is that you do not apply any format to row data that you recieve from data base.
If you want to have specific format you should specyfy it. In documentation you will find Formatting Types, there are all information how to deal with formatting.
If you just want to have result
Console.WriteLine(valueNumber.ToString("N1", CultureInfo.InvariantCulture));
Is this causing display issues or calculation issues?
If you just want the output to display as desired, can you just use valueNumber.ToString("G")?
You can't. You will have to convert it to a String if you want to do that.
select 1123.80 num from dual;
select to_char(1123.80, 9999.99) from dual;
i.e. the only way is
SQL> INSERT INTO MYTABLE(ID,VALUE_NUMBER) VALUES(1,1178.20);
SQL> SELECT to_char(VALUE_NUMBER, 9999.99) NUM FROM MYTABLE
NUM
-------------------
1178.20 (string/varchar)
SQL> SELECT VALUE_NUMBER NUM FROM MYTABLE
NUM
-------------------
1178.2 (number)
This may cause issues while performing number(decimal) operations on the value later since it becomes a string and would require conversion/casting to number again, which is not a very recommended way. even if you delcare the precision to be NUMBER(10,2) from NUMBER(10), results will be same.

C# - InvalidCastException when fetching double from sqlite

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.

Categories