Not accurately read numeric number with a DataReader from SQLite - c#

I got a problem where I can't read the data from my database correctly.
To give you an example, I got the important value that is in the database 6.69879289850025E-06, but I read 6.0 (which is not accurate since it's suppose to be way smaller) in the C# program.
Something similar happen to ImportantValue2 where the value that is in the database is -0,000158976621370616 and in my C# program I get 0,0.
public double ImportantValue1 { get; set; }
public double ImportantValue2 { get; set; }
public string Note { get; set; }
public MyObject(SQLiteDataReader reader)
{
ImportantValue1 = Convert.ToDouble(reader["important_value_1"]); //Value in the database is REAL
ImportantValue2 = Convert.ToDouble(reader["important_value_2"]);//Value in the database is REAL
Note = reader["note"].ToString(); //Value in the database is TEXT
}
Update
And this is how I call it.
using (SQLiteConnection c = new SQLiteConnection(connection))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sqlCommand, c))
{
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
objectFromBD = new MyObject(reader);
}
}
}
}
And the SQLite code
CREATE TABLE "table"(
"id" INTEGER,
"important_value_1" REAL NOT NULL,
"important_value_2" REAL NOT NULL,
"note" TEXT NOT NULL,
PRIMARY KEY ("id" AUTOINCREMENT)
);
Thank you for your help!

reader["important_value_1"] will return index which column exists in your SQL script instead of your expectation value.
reader["important_value_1"] will get the value of the specified column.
You can try to use reader["important_value_1"] in reader.GetDouble function to read your data from columns.
ImportantValue1 = reader.GetDouble(reader["important_value_1"])
ImportantValue2 = reader.GetDouble(reader["important_value_2"])
SqliteDataReader.Item[] Property
Note
You might need to use reader.Read() let the reader point to read the next row if you want.
while (reader.Read()){
//... read all rows from your db
}

Here is a temporary solution I got, but that is not efficient.
I saw that I can read a string with the reader, but I couldn't do it with a double. So I used reader.GetString(rowValue); and than I converted the string to a double using the fonction below.
public double StringToDoubleWithNotation(string value)
{
return Convert.ToDouble(Decimal.Parse(value, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint));
}
Now everything is fine, but I would prefer to see a better solution since doing 3 conversion is not that efficient.

Related

How to properly get index of a custom class list <CustomClass> and check if the list value in the selected index is equal to int [c#]

I have created a custom class list which is filled from the result of a query. Specifically, me query returns (int, timestamp) 2 columns -> 2 values.
public class SucessfulCompletion
{
public int Result;
public string Timestampvalue;
public SucessfulCompletion(int result, string timestamp) => (Result, Timestampvalue) = (result, timestamp);
}
public List<SucessfulCompletion> SuccessfulCalculationsTimestamp(string connectionstring)
{
List<SucessfulCompletion> QueryListResult = new List<SucessfulCompletion>();
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
{
var query_table_timestamp = (
#"SELECT CASE
WHEN t.STATUS = 'SUCCESS' AND t.STATUS_DESCRIPTION = 'ALL QUERIES COMPLETED SUCCESSFULLY' THEN 1
ELSE 0
END SuccessfulCompletion, t.TIMESTAMP
FROM (SELECT TOP (1) l.TIMESTAMP, l.STATUS, l.STATUS_DESCRIPTION
FROM LOG_DETAILS l
ORDER BY 1 DESC) t");
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(query_table_timestamp, sqlConnection))
{
using (SqlDataReader reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
QueryListResult.AddRange(new List<SucessfulCompletion>
{
new SucessfulCompletion(reader.GetInt32(0), reader.GetDateTime(1).ToString())
});
}
reader.Close();
}
sqlCommand.Cancel();
}
}
return QueryListResult;
}
The code to create the custom class list was taken from this SO question
So the QueryListResult would be like [1, "2020-10-04 HH:MM:SS"]
Now I want to make an if statement to check if the first index of the QueryListResult is ether 0 or 1.
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0]=1) //my problem is here
{
//Enable is only if successful
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of success
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[1]);
}
else
{
//add textbox of fail
SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[1]);
}
In the if statement I get an error
Cannot implicitly convert type 'int' to 'TestEnvironment.MainWindow.SucessfulCompletion'
I know it may be a silly question for someone experienced with C# but I am a newbie, so I would appreciate your help. Please inform me in the comments if the theme of the question is a duplicate one I will close the question.
You're comparing an jnstance of your class to a number.
These are different things.
And one equal sign sets rather than compares
Try
If ( reportsucessfulcompletion[0].Result == 1)
You should make these properties rather than variables.
I also recommend Dapper as a "micro" orm very close to the ado metal, but which saves a fair bit of coding whilst implementing best practice for you.

InvalidCastException, fetching enum value from SqlDataReader in ASP.NET MVC

I am getting a System.InvalidCastException at runtime with this code:
inf.Country = (Country)(int)rdr["Country"];
Model class:
public class BasicInfo
{
.....
public Gender Gender { get; set; }
public Country Country { get; set; }
}
public enum Gender
{
Male,
Female
}
public enum Country
{
Australia,
Bangladesh,
England,
France,
Usa
}
DbConnect class: the method return the entities list and it will pass through the controller's view.
usp_GetAllStudentData is a stored procedure that returns the list of records.
public List<BasicInfo> SelectStudent()
{
ConnectionString();//Contain Connection string
List<BasicInfo> entities = new List<BasicInfo>();
SqlCommand cmd = new SqlCommand("usp_GetAllStudentData", conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while(rdr.Read())
{
BasicInfo inf = new BasicInfo();
inf.FirstName = (string)rdr["FirstName"];
.....//other required value are assigned to class members.
//inf.Gender = (Gender)(int)rdr["Gender"];
inf.Country = (Country)(int)rdr["Country"]; // Getting run time exception here
entities.Add(inf);
}
return entities;
}
How the data is stored in the database
Can you guys let me know what is the best method to cast an enum value?
Or let me know if there is any alternative way to fix this issue.
Table Design
If your database field contains a NULL value then the result of your code is the Invalid Cast Exception
For example
object x = DbNull.Value; // A null value on the db is represented by DbNull.Value
inf.Country = (Country)(int)x; // Gives the Invalid Cast Exception
How to fix depends on what you want to do with null values. If you don't allow null values for countries then you should revise your code that accepts an invalid input and block these inputs (and not forget to set the Country field to NOT allow NULL values on the database table)
If you accept a null value then I suggest to add another entry to your country enum
public enum Country
{
Australia = 0,
Bangladesh,
England,
France,
Usa,
// <=== new countries should be added here....
Undefined = 9999
}
and change your reading code to
int ordinal = rdr.GetOrdinal("Country");
inf.Country = rdr.IsDBNull(ordinal) ? Country.Undefined
: (Country)(int)rdr["Country"];
From your comments it seems that you have stored the numeric value of the enum in a varchar column transforming your number in a string
If you want to convert back this string to the appropriate enum value then you should use Enum.TryParse, but the conversion back is not as simple as it seems.
So if you want to still check for null values then:
int ordinal = rdr.GetOrdinal("Country");
// Check for null value....
if(rdr.IsDBNull(ordinal))
inf.Country = Country.Undefined;
else
{
// OK, not null, but what if it is "ABC"??
if(!Enum.TryParse(rdr["Country"].ToString(), out inf.Country))
inf.Country = Country.Undefined;
// OK, but what if it is a number not defined in the coutry enum (IE "88")
if(!Enum.IsDefined(typeof(Country), inf.Country))
inf.Country = Country.Undefined;
}
As you can see, if there isn't a specific reason then I suggest to store the enum value as an integer and not as a string. This allow more flexibility in your code and in future changes to this variable.
try this
if (rdr["Country"]!=DBNull.Value)
{
inf.Country =(Country)Enum.ToObject(typeof(Country) , rdr["Country"].ToString());
}

SQLite exception deleting row using sqlite-net

I got tired to search so here it goes my first SO question hoping someone had the same problem and can help me
Goal
I am trying to store my application data with a SQLite database
Application description
Windows 8 app C# XAML with local SQLite database using SQLite for Windows Runtime Extension and sqlite-net library
Table definition
public class Product {
private int _id;
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public int ID
{
get { return _id; }
set { _id = value; }
}
private string _date;
public string DATE
{
get { return _date; }
set { _date = value; }
}
private string _desc;
public string DESC
{
get { return _desc; }
set { _desc = value; }
}
}
Problem1
public int Insert (object obj) description says the following:
Inserts the given object and retrieves its auto incremented primary key if it has one.
However everytime I insert a row it return 1. I can sucessfully insert with a auto-incremet ID but somehow it does not return me its ID. Why?
Problem 2
I can insert new rows but not delete them
Working around problem 1 to get last row generated ID, I try to delete rows but with no success.
See this example test that always fails:
using (var db = new SQLiteConnection(Path.Combine(_path, _dbname)))
{
var p1 = new Product() { DESC = "insert1", DATE = DateTime.Now.ToString() };
db.Insert(p1);
p1.ID = 1;
var p2 = new Product() { DESC = "insert2", DATE = DateTime.Now.ToString() };
// I am sure that this row is getting ID 2, so it will not have a wrong ID
p2.ID = 2;
db.Insert(p2);
var p3 = new Product() { DESC = "insert3", DATE = DateTime.Now.ToString() };
db.Insert(p3);
p3.ID = 3;
db.Delete<Product>(p2);
}
As you can see I try to insert 3 rows and delete the second one. The rows are inserted but I get the following SQLite.SQLiteException exception:
unable to close due to unfinalized statements or unfinished backups
Why? I don't open other connections before and after that.
Thanks in advance
Solved
Problem 1
+1 and thanks for #Bridgey for pointing out that function does not match it description and for the relevant search
The function does not return ID as it says but it defines the object ID. So when I insert a new Product, Product.ID will have last inserted ID.
Problem 2
I changed db.Delete<Product>(p2); to db.Delete(p2); and now it works. SQLite-net correctly identify the row as Product. I still don't know why the unable to close due to unfinalized statements or unfinished backups exception was happening. If someone knows why tell me please.
I think for problem 2, the issue is that you are passing the Product object as the parameter for the Delete method. The documentation says: Deletes the object with the specified primary key. I think the following should work:
db.Delete<Product>(p1.ID);
Regarding problem 1, the code of the Insert method of the sqlite-net package ends:
var count = insertCmd.ExecuteNonQuery (vals);
if (map.HasAutoIncPK) {
var id = SQLite3.LastInsertRowid (Handle);
map.SetAutoIncPK (obj, id);
}
return count;
As you can see, count is returned, even if id is set.
EDIT: Actually, according to the author this is deliberate.
"Insert returns the number of rows modified. The auto incremented columns are stored in the object. Please see the doc comments."
https://github.com/praeclarum/sqlite-net/issues/37

c# database selecting

Im having a table called transaktions where i inserted customers billings and customer number. My primary key is an int id that has identity specifikation. My question is how do i select all rows that contains a specific customer_nr and returns all the results?
Right now im doing:
public string getCustomerTransaktions(string CustNr)
{
using (var cmd = new SqlCommand("select billing_name from [transaktions] where customer_nr = #customer_nr", Connect()))
{
cmd.Parameters.AddWithValue("#customer_nr", custNr);
using (var er = cmd.ExecuteReader())
{
if (er.Read())
{
return (string)er["billing_name"];
}
}
}
return "UNKNOWN";
}
This will only print the first row that matches the customer nr, there are atleast 20 rows left. Anyone have any suggestion that will help me?
Best Regards
Agree both with #Niklas and #Skurmedel, you need to both use a loop for processing multiple records and collate them together before returning them as a result.
e.g.
public List<string> getCustomerTransaktions(string CustNr)
{
List<string> names = new List<string>();
using (var cmd = new SqlCommand("select billing_name from [transaktions] where customer_nr = #customer_nr", Connect()))
{
cmd.Parameters.AddWithValue("#customer_nr", custNr);
using (var er = cmd.ExecuteReader())
{
while(er.Read())
{
names.Add((string)er["billing_name"]);
}
}
}
return names;
}
Since you do return ... and your method specification pretty much limits the results to one particular value, you can never return more than one billing name. It will return a name as soon as a row has been read, or your default if no rows were returned.
You should put the values in a list or likewise and return that.
try to change the if to a while
while (er.Read())
I guesss a simple solution would be to return the whole ResultSet instead of tring to work around with string. Then iterate through its all items from your calling method

What to name 2 methods with same signatures

Initially I had a method in our DL that would take in the object it's updating like so:
internal void UpdateCash(Cash Cash)
{
using (OurCustomDbConnection conn = CreateConnection("UpdateCash"))
{
conn.CommandText = #"update Cash
set captureID = #captureID,
ac_code = #acCode,
captureDate = #captureDate,
errmsg = #errorMessage,
isDebit = #isDebit,
SourceInfoID = #sourceInfoID,
PayPalTransactionInfoID = #payPalTransactionInfoID,
CreditCardTransactionInfoID = #CreditCardTransactionInfoID
where id = #cashID";
conn.AddParam("#captureID", cash.CaptureID);
conn.AddParam("#acCode", cash.ActionCode);
conn.AddParam("#captureDate", cash.CaptureDate);
conn.AddParam("#errorMessage", cash.ErrorMessage);
conn.AddParam("#isDebit", cyberCash.IsDebit);
conn.AddParam("#PayPalTransactionInfoID", cash.PayPalTransactionInfoID);
conn.AddParam("#CreditCardTransactionInfoID", cash.CreditCardTransactionInfoID);
conn.AddParam("#sourceInfoID", cash.SourceInfoID);
conn.AddParam("#cashID", cash.Id);
conn.ExecuteNonQuery();
}
}
My boss felt that creating an object every time just to update one or two fields is overkill. But I had a couple places in code using this. He recommended using just UpdateCash and sending in the ID for CAsh and field I want to update. Well the problem is I have 2 places in code using my original method. And those 2 places are updating 2 completely different fields in the Cash table. Before I was just able to get the existing Cash record and shove it into a Cash object, then update the properties I wanted to be updated in the DB, then send back the cash object to my method above.
I need some advice on what to do here. I have 2 methods and they have the same signature. I'm not quite sure what to rename these because both are updating 2 completely different fields in the Cash table:
internal void UpdateCash(int cashID, int paypalCaptureID)
{
using (OurCustomDbConnection conn = CreateConnection("UpdateCash"))
{
conn.CommandText = #"update Cash
set CaptureID = #paypalCaptureID
where id = #cashID";
conn.AddParam("#captureID", paypalCaptureID);
conn.ExecuteNonQuery();
}
}
internal void UpdateCash(int cashID, int PayPalTransactionInfoID)
{
using (OurCustomDbConnection conn = CreateConnection("UpdateCash"))
{
conn.CommandText = #"update Cash
set PaymentSourceID = #PayPalTransactionInfoID
where id = #cashID";
conn.AddParam("#PayPalTransactionInfoID", PayPalTransactionInfoID);
conn.ExecuteNonQuery();
}
}
So I thought hmm, maybe change the names to these so that they are now unique and somewhat explain what field its updating:
UpdateCashOrderID
UpdateCashTransactionInfoID
ok but that's not really very good names. And I can't go too generic, for example:
UpdateCashTransaction(int cashID, paypalTransactionID)
What if we have different types of transactionIDs that the cash record holds besides just the paypalTransactionInfoID? such as the creditCardInfoID? Then what? Transaction doesn't tell me what kind. And furthermore what if you're updating 2 fields so you have 2 params next to the cashID param:
UpdateCashTransaction(int cashID, paypalTransactionID, someOtherFieldIWantToUpdate)
see my frustration? what's the best way to handle this is my boss doesn't like my first route?
Why not just:
UpdateCashPaymentSource(int cashID, int PayPalTransactionInfoID)
UpdateCashCapture(int cashID, int paypalCaptureID)
My boss felt that creating an object every time just to update one or two fields is overkill.
He would be right, if you have to create the object every time. The correct response to this is that you should already be using these business objects throughout your app. You don't create a new Cash object. You pass it the Cash object you already have to be saved.
"UpdateCashWithCapture" and "UpdateCashWithTransaction"?
UpdateCashByTransactionInfoID
UpdateCashByCaptureID()
?
Would one method and an enum cut it?
internal void UpdateCash(int cashID, int id, FieldName field)
{
using (OurCustomDbConnection conn = CreateConnection("UpdateCash"))
{
conn.CommandText = string.format("update Cash set {0} = #id where id = #cashID", field.ToString());
conn.AddParam("#id", id);
conn.AddParam("#cashId", cashId);
conn.ExecuteNonQuery();
}
}
public enum FieldName
{
PayPalCaptureId,
PayPalTransactionInfoID
}
EDIT:
On now reading your edit, I would agree that your original approach would be the way to go, in my opinion - passing in an object and updating all of the associated fields in a database compared to passing in an object property value and updating that in a database, the biggest performance killer will be opening the database connection, not the number of fields relating to one database record.
How about UpdateCashByCaptureID and UpdateCashByTransactionInfoID?
Add the name of the field being updated, i.e.
internal void UpdateCash_paypalCaptureID(...)
internal void UpdateCash_PayPalTransactionInfoID(...)
You could encapsulate the update query logic in a class:
public abstract class CashUpdateQuery
{
public CashUpdateQuery(int cashId)
{
this.CashId = cashId;
}
protected int CashId { get; private set; }
public abstract void SetConnectionProperties(OurCustomDbConnection conn);
}
Then you can have specific subclasses for each update scenario. So for your PayPal query you'd have something like this:
public PaypalTransactionCashUpdateQuery : CashUpdateQuery
{
private readonly int paypalCaptureId;
public PaypalTransationCashUpdateQuery(int cashId, int paypalCaptureId)
{
this.paypalCaptureId = paypalCaptureId;
}
public override void SetConnectionProperties(OurCustomDbConnection conn)
{
conn.CommandText = #"update Cash
set CaptureID = #paypalCaptureID
where id = #cashID";
conn.AddParam("#captureID", this.paypalCaptureId);
conn.AddParam("#cashID", this.CashId);
}
}
Then your update method can take a query object and use it to set the query properties on the connection and execute it:
internal void UpdateCash(CashUpdateQuery query)
{
using(OurCustomDbConnection conn = CreateConnection("UpdateCash"))
{
query.SetConnectionProperties(conn);
conn.ExecuteNonQuery();
}
}
This means that adding new queries is simply a case of adding a new subclass of CashUpdateQuery.

Categories

Resources