I'm playing around with SQlite and sql commands. I'm trying to make a quizz program and I have a loop, that reads the questions and answers from my database and adds them to a list. I also have a bool that defines if the answer picked from the database is right or wrong.
My problem is that all this works fine the first time my loop executes the code and adds the true and false to my array of bools, but the 2'nd time my loop executes it throws the exception: SPECIFIED CAST NOT VALID. The method that fails looks like this: I made a comment where the code fails:
public void GetQuestion(int categoryRef)
{
Console.Clear();
int arrayIndex = 0;
int qListIndex = 0;
int idListIndex = 0;
List<string> qList = new List<string>();
List<int> idList = new List<int>();
int ansNr = 1;
bool[] isTrue = new bool[3];
SQLiteDataReader sqReader;
SQLiteCommand sqCommand = new SQLiteCommand(sqConnection);
try
{
sqCommand.CommandText = "SELECT Question, ID FROM Questions WHERE CategoryRef=" + categoryRef.ToString();
sqCommand.Connection.Open();
sqReader = sqCommand.ExecuteReader();
foreach (var item in sqReader)
{
qList.Add(sqReader.GetString(0));
idList.Add(sqReader.GetInt32(1));
}
sqReader.Close();
}
finally
{
sqConnection.Close();
}
for (int i = 0; i < qList.Count; i++)
{
try
{
sqCommand.CommandText = "SELECT Answer FROM Answers WHERE QuestionRef=" + idList[idListIndex].ToString();
sqConnection.Open();
sqReader = sqCommand.ExecuteReader();
Console.WriteLine(qList[qListIndex]);
foreach (var answer in sqReader)
{
Console.WriteLine(ansNr + ":" + sqReader.GetString(0));
ansNr++;
}
sqReader.Close();
}
finally
{
sqConnection.Close();
}
try
{
//THIS CODE FAILS 2'nd TIME IT LOOPS THROUGH
sqCommand.CommandText = "SELECT IsTrue FROM Answers WHERE QuestionRef=" + idList[idListIndex].ToString();
sqConnection.Open();
sqReader = sqCommand.ExecuteReader();
foreach (var item in sqReader)
{
isTrue[arrayIndex] = sqReader.GetBoolean(0); //<-- Specified cast is not valid.
arrayIndex++;
}
sqReader.Close();
}
finally
{
sqConnection.Close();
}
string input = Console.ReadLine();
int number = Convert.ToInt32(input);
switch (number)
{
case 1:
if (isTrue[0] == true)
{
Console.WriteLine("Correct");
}
if (isTrue[0] == false)
{
Console.WriteLine("False");
}
break;
case 2:
if (isTrue[1] == true)
{
Console.WriteLine("Correct");
}
if (isTrue[1] == false)
{
Console.WriteLine("False");
}
break;
case 3:
if (isTrue[2] == true)
{
Console.WriteLine("Correct");
}
if (isTrue[2] == false)
{
Console.WriteLine("False");
}
break;
}
Console.ReadLine();
idListIndex++;
qListIndex++;
arrayIndex = 0;
ansNr = 1;
}
}
Mostly likely, that you read DbNull from database, which cannot be cast to bool.
SQLite does not guarantee that the contents of any column will match the cast.
The GetBoolean is failing. You have 3 choices.
Try Catch
Test for Null
Replace your select with a query guaranteed to return true/false
SQLite version 3.7.8 2011-09-19 14:49:19
sqlite> CREATE TABLE Q (A);
sqlite> INSERT INTO Q VALUES (0);
sqlite> INSERT INTO Q VALUES (1);
sqlite> INSERT INTO Q VALUES ('T');
sqlite> INSERT INTO Q VALUES ('F');
sqlite> INSERT INTO Q VALUES ('Y');
sqlite> INSERT INTO Q VALUES (NULL);
sqlite> SELECT not ifnull(A==0 OR A=='F' OR A=='N',1) FROM Q;
0
1
1
0
1
0
In my case, EF was throwing the same error ("Specified cast not valid") because it couldnt resolve "1234" that SQLite was throwing at it.
The solution was to change the type of Name field from STRING to TEXT.
I was getting the same error because SQLite was returning a NULL.
Check for null: if (!sqlite_datareader.IsDBNull(4))
{// then do what is required}
Related
I am trying to tweak the below code for checking whether a record exist or not in the database.
private bool IsAlreadyExist(string LicenseType, int JurisdictionId)
{
LicenceBL lbl = new LicenceBL(0);
DataSet lds = new DataSet();
lbl.FetchForEdit(lds, AgentId, BrokerId);
if (lds.Tables[0].Select('LicenseType='+LicenseType+'and Jurisdiction='+JurisdictionId).Count > 0)
{
return true;
}
else
{
return false;
}
}
It is not working. It throws error... Please help me !!!
Nida, try this:
(1)
if (lds.Tables[0].Select('LicenseType='+LicenseType+'and Jurisdiction='+JurisdictionId).Length > 0) {
}
(2)
LINQ
int count = lds.Tables[0].AsEnumerable()
.Where(x => x["LicenseType"] == YourValue && y => y["Jurisdiction"]==YourValue).ToList().Count;
if (count >0) { // do your stuff}
Select returns an array of rows and Count comes from Linq. So you need to call it as a method Count() not as a property.
if (lds.Tables[0].Select('LicenseType='+LicenseType+
'and Jurisdiction='+JurisdictionId).Count() > 0)
{
Also instead of if/else, following statement, is sufficient:
return lds.Tables[0].Select('LicenseType='+LicenseType+
'and Jurisdiction='+JurisdictionId).Count() > 0;
I am using Sqlite.Net in my Xamarin.Forms application. So far it has been great at returning lists of objects if my object is a class like so:
SqliteDatabase.Connection.Query<Customer>("Select * from Customers");
I would now like to return the equivalent of a DataSet dynamically from my query
SqliteDatabase.Connection.Query("Select * from Customers inner join Calls on Customers.Id=Calls.CustomerId")
Now from the second query I would like to return a DataSet instead of a list of objects. I know I could create a new object which combines the columns of Customers and Calls but I don't want to have to create objects every time I want to query the database.
Is it possible to just dynamically return a Dataset or Object?
In the end I actually managed to come up with a method that will run any query and return the rows as items in the list and the columns as objects in the array:
public List<object[]> RunSql(string sqlString, bool includeColumnNamesAsFirstRow)
{
var lstRes = new List<object[]>();
SQLitePCL.sqlite3_stmt stQuery = null;
try
{
stQuery = SQLite3.Prepare2(fieldStrikeDatabase.Connection.Handle, sqlString);
var colLenght = SQLite3.ColumnCount(stQuery);
if (includeColumnNamesAsFirstRow)
{
var obj = new object[colLenght];
lstRes.Add(obj);
for (int i = 0; i < colLenght; i++)
{
obj[i] = SQLite3.ColumnName(stQuery, i);
}
}
while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
{
var obj = new object[colLenght];
lstRes.Add(obj);
for (int i = 0; i < colLenght; i++)
{
var columnType = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);
switch (columnType)
{
case "text":
obj[i] = SQLite3.ColumnString(stQuery, i);
break;
case "int":
obj[i] = SQLite3.ColumnInt(stQuery, i);
break;
case "real":
obj[i] = SQLite3.ColumnDouble(stQuery, i);
break;
case "blob":
obj[i] = SQLite3.ColumnBlob(stQuery, i);
break;
case "null":
obj[i] = null;
break;
}
}
}
return lstRes;
}
catch (Exception)
{
return null;
}
finally
{
if (stQuery != null)
{
SQLite3.Finalize(stQuery);
}
}
}
SQLite.NET PCL is a .NET wrapper around sqlite.
Therefore you can query similar to EF by using a join in in LINQ or Lambda than in the Query. The wrapper will handle the conversion to sqlite query for you.
You can then return a new datatype of the joined type or a dynamic type.
Note : Joins are not directly supported in sqlite (more info) and work around is mentioned here.
Sample code:
var conn = new SQLiteConnection(sqlitePlatform, "foofoo");
var query = from customer in conn.Table<Customers>().ToList()
join call in conn.Table<Calls>().ToList()
on customer.ID equals call.CustomerId
select new { Customer = customer , Calls = call };
Lambda version:
conn.Table<Customer>().ToList().Join
(conn.Table<Call>().ToList(),
customer => customer.Id,
call => call.CustomerId,
(customer, call) => new { Customer = customer, Calls = call });
thank u so much user1! works perfect.
here is just an example how to use ur method:
var objects = mySQLiteConnection.RunSql("SELECT * FROM Persons", true);
// ColumnNames
List<string> ColumnNames = new List<string>();
for (int column = 0; column < objects[0].Length; column++)
{
if (objects[0][column] != null) spaltennamen.Add((string)objects[0][column]);
}
// RowValues
for (int row = 1; row < objects.Count; row++)
{
for (int column = 0; column < objects[row].Length; column++)
{
if (objects[row][column] != null) System.Diagnostics.Debug.WriteLine(spaltennamen[column] + " : " + objects[row][column]);
}
}
It sounds like what you want to do is essentially recreate ADO.NET. When you say "DataSet", I'm guessing that you are talking about ADO.NET. This probably means that you don't want to use the ORM functionality built in to the SQLite.Net library.
I have created this version of the library that will allow you to do flat table reads from an SQLite database. It means that you CAN read the data in to an ADO.NET dataset if you like.
https://github.com/MelbourneDeveloper/SQLite.Net.Standard
Unlike #Fabian Monkemoller, i was unable to get #User1's code to work straight away. This is a modified version that make use of nullable reference types and method-nesting to seperate the main-code from the try-catch block:
public static object?[][]? ToDataSet(this SQLiteConnection sqlConnection, string query , bool includeColumnNamesAsFirstRow = true)
{
var stQuery = SQLite3.Prepare2(sqlConnection.Handle, query );
var colLength = SQLite3.ColumnCount(stQuery);
try
{
return SelectRows().ToArray();
}
catch (Exception e)
{
return null;
}
finally
{
if (stQuery != null)
{
SQLite3.Finalize(stQuery);
}
}
IEnumerable<object?[]> SelectRows()
{
if (includeColumnNamesAsFirstRow)
{
yield return SelectColumnNames(stQuery, colLength).ToArray();
}
while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
{
yield return SelectColumns(stQuery, colLength).ToArray();
}
static IEnumerable<object> SelectColumnNames(SQLitePCL.sqlite3_stmt stQuery, int colLength)
{
for (int i = 0; i < colLength; i++)
{
yield return SQLite3.ColumnName(stQuery, i);
}
}
static IEnumerable<object?> SelectColumns(SQLitePCL.sqlite3_stmt stQuery, int colLength)
{
for (int i = 0; i < colLength; i++)
{
var x = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);
yield return x switch
{
"text" => SQLite3.ColumnString(stQuery, i),
"integer" => SQLite3.ColumnInt(stQuery, i),
"bigint" => SQLite3.ColumnInt64(stQuery, i),
"real" => SQLite3.ColumnDouble(stQuery, i),
"blob" => SQLite3.ColumnBlob(stQuery, i),
"null" => null,
_ => throw new Exception($"Unexpected type encountered in for query {stQuery}")
};
}
}
}
}
I'm using C# and I have a function for check this values. ( Name, Identity Number And School Number )
My records on text file.
If two or more same name and surname (different identity number and school number) in text file, I need to show all of them.
How Can I do it with loop ?
Note = identityBox is my TextBox name and identity number in there. This code just run for one record. This is identityFounder Code. Name and Surname founder Code is same with this. I will use next and previous buttons for see all records. Here is the C# Codes. Help me please. Thank you.
void identityFounder()
{
int inout = 0;
double identityNo = Convert.ToDouble(founderBox.Text);
String[] line = File.ReadAllLines("C:\\OgrenciBilgisi_v2.txt");
for (int i = 0; i < line.Length; i++)
{
if (line[i].Contains(identityNo.ToString()))
{
temp = i;
inout = 1;
break;
}
else
{
inout = 0;
continue;
}
}
if (inout == 1)
{
name.Text = line[temp - 3];
surname.Text = line[temp - 2];
address.Text = line[temp - 1];
identity.Text = line[temp];
school.Text = line[temp + 1];
number.Text = line[temp + 2];
faculty.Text = line[temp + 3];
deparrtment.Text = line[temp + 4];
class.Text = line[temp + 5];
}
else
{
MessageBox.Show("Record cannot found file.","Warning",MessageBoxButtons.OK
,MessageBoxIcon.Information);
}
}
First, a couple of quick asides:
inout should be a bool, not an int.
You can drop the else block; it isn't doing anything.
Now, more broadly, what you want to do is keep track of which names you've already seen as you parse the records. This is actually going to be faster and easier if you parse all of the identities at once. Since your lines are not structured except by order, the only way to do this is with a state machine.
I would just make a small console app that parses the text file, looking for duplicate names, and if it finds any then report the file name (or print the entire file to the screen or whatever; I'm not clear on what you mean by "show all of them"). Maybe something like:
struct Record
{
public string Name, Surname;
public int Identity, School;
}
class Program
{
static void Main(string[] args)
{
const string path = #"C:\OgrenciBilgisi_v2.txt";
var position = 0;
var record = new Record();
var records = new Dictionary<string, Record>(); // key is the concatenation of name and surname
using (var reader = new StreamReader(path))
{
var line = reader.ReadLine();
if (line == "---") // TODO use your record delimiter here
{
// start of new record. compare the existing one and commit it if all fields have been written to
if (position == 9)
{
// compare records
var key = record.Name + record.Surname;
if (records.ContainsKey(key))
{
// this is a duplicate student name. confirm that the id and school # are different
if (records[key].Identity == record.Identity &&
records[key].School == record.School)
{
// id and school are the same, so this is an entirely duplicate record
}
else
{
// id and school are different, but name and surname are the same
// TODO: show all records
return;
}
}
else
{
records.Add(key, record);
}
}
else
{
Console.Error.WriteLine("Not all fields in record. Malformed data.");
}
position = 0;
record = new Record();
}
else
{
switch (position)
{
case 0:
record.Name = line;
break;
case 1:
record.Surname = line;
break;
case 3:
int id;
if (int.TryParse(line, out id))
{
record.Identity = id;
} // else there's an error
break;
case 4:
int school;
if (int.TryParse(line, out school))
{
record.School = school;
} // else there's an error
break;
}
position++;
}
}
}
}
This assumes that your data is pretty well formatted; adding error checking is left as an exercise for the reader :)
I'm using LINQ to read and display result from 3 different csv files. The first file is CustomerInfo. Second is PackagePrice and third is HolidayTrans. I need to display result in the listbox based on startDate. My listBox only displays the first record. Here's my LINQ and for loop:
string[] myHolidayTransactionFile = File.ReadAllLines(#"c:\temp\HolidayTrans.csv");
//create an undefined variable myQuery1
//to read each line in the HolidayTrans file
var myQuery1 = from myline in myHolidayTransactionFile
let myField = myline.Split(',')
let id = myField[0]
let startDate = myField[1]
let numOfAdults = myField[2]
let numOfKids = myField[3]
where cid == id
orderby DateTime.Parse(startDate) descending
select new
{
cid,
startDate,
numOfAdults,
numOfKids
};
lstInfo.Items.Add(string.Format(formatLine, "Start Date", "End Date", "Adult Amt", "Kid Amt"));
foreach (var personRecord in myQuery1)
{
startTourDate = personRecord.startDate;
break;
}
//putting logic for getting break on a year every time before it changes
foreach (var personRecord in myQuery1)
{
//personRecord StartDate is equal to the Start tour Date which is selected
if (personRecord.startDate == startTourDate)
{
getNumofDaysTwinAdultPrSingleAdultPr(startTourDate, ref numOfDays, ref twoAdultPr, ref oneAdultPr);
//performing calculations for endate adult amt and kid amt
EndDate = Convert.ToDateTime(startTourDate).AddDays(numOfDays);
if (int.Parse(personRecord.numOfAdults) == 2)
{
adultAmt = twoAdultPr * 2;
}
if (int.Parse(personRecord.numOfAdults) == 1)
{
adultAmt = oneAdultPr;
}
if (int.Parse(personRecord.numOfKids) == 2)
{
kidsAmt = kidsPr * 2;
}
if (int.Parse(personRecord.numOfKids) == 1)
{
kidsAmt = kidsPr;
}
if (int.Parse(personRecord.numOfKids) == 0)
{
kidsAmt = 0;
}
// the subtotal is equal to subtotal + adultamt, it continues till the value stored in the starttourdate
//is same as that of ("transition .name") but as soon as start date changes this condition
//gets false and it get transfer to the else part
subtotalA = subtotalA + adultAmt;
subtotalK = subtotalK + kidsAmt;
//displaying output in a listbox
lstInfo.Items.Add(string.Format(formatLine1, startTourDate, EndDate, adultAmt, kidsAmt));
}
Please help. Thanks in advance
It might be because break; is the second command in your foreach statement. Therefore, only the first record is being printed because in your next foreach statement theres only one date that matches. You need to have only one foreach statement. In other words remove all this:
break;
}
//putting logic for getting break on a year every time before it changes
foreach (var personRecord in myQuery1)
{
I have an issue with a Loop that doesn't actually loop. I've posted a simplified version of my code below. Basically, using the NPOI excel library, I have an excel file with data on the first and second sheet, so I need to do a loop to get through both sheets.
Below is what I have done so far, however this only works through the first sheet and then exits. It fails to increment the variable w. As you can see, there are other loops implemented in this code which function fine so I don't get it.
It's been a very long day and perhaps I'm missing something very simple. I could have it placed wrong or something. If anyone else can spot what I might be doing wrong I'd be very grateful :)
public class SalesFileProcessor : ISalesProcessor
{
public List<FTPSalesRow> ProcessSalesFile(string filename)
{
try
{
using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read))
{
int numberOfSheets = 2;
//Loop through sheets - does not work
for (int w = 0; w <= numberOfSheets; w++)
{
HSSFWorkbook templateWorkbook = new HSSFWorkbook(fs);
HSSFSheet sheet = templateWorkbook.GetSheetAt(w);
HSSFRow row = null;
for (int i = 1; i <= sheet.LastRowNum; i++)
{
FTPSalesDetails t = null;
int currentColumn = 0;
try
{
ModelContainer ctn = new ModelContainer();
row = sheet.GetRow(i);
if (row == null)
{
continue;
}
t = new FTPSalesDetails
{
RowNumber = i,
InvoiceDate = GetCellValue(row.GetCell(0)),
CountrySoldIn = GetCellValue(row.GetCell(1)),
NetUnitsSold = GetCellValue(row.GetCell(2)),
Item = GetCellValue(row.GetCell(3)),
ProductCode = GetCellValue(row.GetCell(5)),
};
if (t.ProductCode == null && t.NetUnitsSold == null)
{
return null;
}
int Qty = int.Parse(t.NetUnitsSold);
for (int x = 0; x < Qty; x++)
{
ItemSale ts = new ItemSale
{
ItemID = GetItemID(t.ProductCode),
ManufacturerID = GetManufacturerID("Samsung"),
DateSold = DateTime.Now,
};
ctn.AddToItemSales(ts);
ctn.SaveChanges();
}
}
catch (IndexOutOfRangeException) { }
}
} //End Loop - the one that doesn't work
}
}
catch (IOException exp)
{
throw new FTPSalesFileProcessingException("Could not open the Sales data file", exp);
}
catch (Exception exp)
{
throw new FTPSalesFileProcessingException("An unknown eror occured during processing the file", exp);
}
return null;
}
if (t.ProductCode == null && t.NetUnitsSold == null)
{
return null;
}
I'm going to guess that this is being hit, causing your entire function to exit. If you are trying to exit out of that iteration of the for loop try a break; instead, or a continue as Mike M pointed out in the comments.
For what you say, assuming your variables are all ok that is the loop is not empty... have you checked you're not hiting this line in the first iteration?
if (t.ProductCode == null && t.NetUnitsSold == null)
{
return null;
}
Looking at the code, the only obvious thing sticking out to me is:
HSSFSheet sheet = templateWorkbook.GetSheetAt(w);
HSSFRow row = null;
for (int i = 1; i <= sheet.LastRowNum; i++)
I would guess that sheet.LastRowNum either equals 0 or 1.
Maybe the indexOutOfRangeException is thrown and thats beacuse you have only one iteration, or instead of <= you sholud use <. Does the sheet numbers start with zero ?