Recursive method call results in StackOverflow exception - c#

Having written this question out, and created a MCVE, it sounds a bit like homework, but really it isn't...
I'm unfortunately too old to be set homework.
I'm trying to write a small app to populate a database I'm working on with a 'pyramid' structure.
There's one original member, who refers 10 members.
Each of these referrals can have 10 referrals. Each of those, has 10 referrals. And so on...
I'm trying to fill the database with a maximum number of members (which is supplied)
If I set the maximum number of members to 100,000 - this works.
200,000 however, throws a StackOverflow exception.
I'm pretty sure it's down to me not terminating the 'fanning out' early enough. But I can't for the life of me figure out where.
Note, my MCVE below is using Dapper for simplicity sake, hence the simple INSERT INTO statement
public class MemberPopulator
{
private readonly SqlConnection connection;
private const string MemberSql = #"
INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created)
VALUES (#FirstName, #LastName, #ReferralId, #Active, #Created);
SELECT CAST(SCOPE_IDENTITY() as int)";
private int TotalMemberCount;
private const int MaxMemberCount = 200000;
public MemberPopulator()
{
connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
}
public void CreateMembers()
{
//clear members
connection.Execute("TRUNCATE TABLE Members");
//create the 'original' member (top of pyramid)
var originalMemberId = connection.Query<int>(MemberSql, new Member
{
FirstName = "FirstName Goes Here",
ReferralId = 0
}).Single();
//now we have 1 total members
TotalMemberCount = 1;
//recursively create members, starting with original member,
RecursiveCreate(new[] { originalMemberId });
}
private void RecursiveCreate(IEnumerable<int> referralMemberIds)
{
//don't recurse if we've already got enough members
if (TotalMemberCount >= MaxMemberCount)
return;
foreach (var referralId in referralMemberIds)
{
//Create 10 members
var refs = CreateReferredMembers(referralId, 10);
RecursiveCreate(refs);
}
}
private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
{
var referredMemberIds = new List<int>();
for (var i = 0; i < numberOfReferrals; i++)
{
if (TotalMemberCount >= MaxMemberCount)
break;
var member = new Member
{
FirstName = "FirstName Goes Here",
ReferralId = referralId
};
var memberId = connection.Query<int>(MemberSql, member).Single();
referredMemberIds.Add(memberId);
TotalMemberCount++;
}
return referredMemberIds;
}
}

The stack in C# is set to 1MB for 32bit applications or 4MB for 64bit applications by default. This is suitable for most applications. In case you need more please follow the guidance in the net (for example this one).
In case you do not know exactly the level of recursion I would suggest to simulate recursion by using a Stack or Queue datatype.
public class MemberPopulator
{
private readonly SqlConnection connection;
private const string MemberSql = #"
INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created)
VALUES (#FirstName, #LastName, #ReferralId, #Active, #Created);
SELECT CAST(SCOPE_IDENTITY() as int)";
private int TotalMemberCount;
private const int MaxMemberCount = 200000;
public MemberPopulator()
{
connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
}
public void CreateMembers()
{
//clear members
connection.Execute("TRUNCATE TABLE Members");
//create the 'original' member (top of pyramid)
var originalMemberId = connection.Query<int>(MemberSql, new Member
{
FirstName = "FirstName Goes Here",
ReferralId = 0
}).Single();
//now we have 1 total members
TotalMemberCount = 1;
//recursively create members, starting with original member,
NonRecursiveCreate(originalMemberId);
}
private void NonRecursiveCreate(int root)
{
Queue<int> members = new Queue<int>();
members.Enqueue(root);
while (members.Any() && TotalMemberCount < MaxMemberCount)
{
var referralId = members.Dequeue();
//Create 10 members
var refs = CreateReferredMembers(referralId, 10);
foreach (int i in refs)
{
members.Enqueue(i);
}
}
}
private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
{
var referredMemberIds = new List<int>();
for (var i = 0; i < numberOfReferrals; i++)
{
if (TotalMemberCount >= MaxMemberCount)
break;
var member = new Member
{
FirstName = "FirstName Goes Here",
ReferralId = referralId
};
var memberId = connection.Query<int>(MemberSql, member).Single();
referredMemberIds.Add(memberId);
TotalMemberCount++;
}
return referredMemberIds;
}
}

Related

How do I remove a line from the list based on the ID of that line?

I've been trying to figure this out for the past few days, but I just can't seem to get it work.
So I have a txt file which has this format:
id;könyvcím;szerző;kiadó;kiadási év;
I am using a structs and a list such as this:
public static List<Books> BooksList = new List<Books>();
public struct Books
{
public int id;
public string title;
public string writer;
public string publisher;
public int published_year;
}
And I'm also putting all these into a List based on the struct like this:
StreamReader booksRead = new StreamReader("konyvek.txt", Encoding.UTF8);
booksRead.ReadLine();
while (!booksRead.EndOfStream)
{
string[] split = booksRead.ReadLine().Split(';');
Books inRead = new Books();
inRead.id = Convert.ToInt32(split[0]);
inRead.title = split[1];
inRead.writer = split[2];
inRead.publisher = split[3];
inRead.published_year = Convert.ToInt32(split[4]);
BooksList.Add(inRead);
}
booksRead.Close();
All I want is, for example, to find where the line with ID 2 is, and remove that line from my textfile. I've tried to get the index of the line I want, and remove it like that from my textfile, but it even fails to get the index, I tried using IndexOf, FindIndex and trying to go on a loop. I'm pretty sure my struct is not happy with me for using it like that because I get errors such as this when I run my code:
System.InvalidCastException: 'Unable to cast object of type 'Books' to
type 'System.IConvertible'.'
Here is the way I'm trying to get the index of the line I want to remove
Books item = new Books();
for (int i = 0; i < BooksList.Count; i++)
{
if (Convert.ToInt32(textBox_id_delete.Text) == item.id)
{
RemoveAt = item.id;
}
}
int index = BooksList.FindIndex(x => Convert.ToInt32(x) == RemoveAt);
MessageBox.Show(Convert.ToString(index));
I'm pretty sure I'm approaching this extremely wrong, and I'd accept any kind of help.
You are doing it completely wrong for a number of reasons.
First, how would you do that the way you are doing:
void Main()
{
var filename = #"c:\myFolder\mybooklist.txt";
// read into an enumerable
var books = File.ReadAllLines(filename)
.Select(x => x.Split(';'))
.Select(x => new Book {
Id = int.TryParse(x[0], out int bookId)?bookId:0,
Title = x[1],
Writer = x[2],
Publisher = x[3],
Published_year=int.TryParse(x[4], out int year)?year:0
});
// remove the one with id 2
// and save back
var otherBooks = books.Where(b => b.Id != 2);
File.WriteAllLines(filename, otherBooks.Select(b => $"{b.Id};{b.Title};{b.Writer};{b.Publisher};{b.Published_year}"));
}
public struct Book
{
public int Id;
public string Title;
public string Writer;
public string Publisher;
public int Published_year;
}
And now what is wrong with this.
A text file is not a database but you are trying to use a text file as a database.
With a text file, you are not actually doing any control here, if the ID is unique or not (there might be N books with the ID 2).
(Side matter) You are using C#, but looks like you are coming from another language and not using the naming conventions at all.
IMHO, instead you should simply use a database, an embedded one for example like LiteDb or Sqlite. If you care to see a sample with LiteDb or Sqlite, let me know.
EDIT: I am adding SQLite and LiteDb samples. In either case, you would need to add Sqlite.Data.Sqlite and LiteDB respectively from Nuget and add using statements.
In case of SQLite, please note that you could use Linq adding some drivers. I directly used the ADO.Net commands and didn't use a Book class for mapping.
LiteDB, being a NoSQL database written in C# for C#, can directly use objects and support Linq out of the box.
Samples show only the surface for both.
SQLite sample:
private static readonly string dataFile = #"d:\temp\books.s3db";
void Main()
{
CreateDb(dataFile);
SeedSampleData(dataFile);
// List the current data
Console.WriteLine("Current Data");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
DeleteSampleRow(dataFile);
// List the current data
Console.WriteLine("After deleting");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
string deleteById = "delete from books where id = #id";
string deleteByTitle = "delete from books where Title = #title";
string deleteByWriter = "delete from books where Writer = #writer";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmdById = new SQLiteCommand(deleteById, cn))
using (SQLiteCommand cmdByTitle = new SQLiteCommand(deleteByTitle, cn))
using (SQLiteCommand cmdByWriter = new SQLiteCommand(deleteByWriter, cn))
{
cmdById.Parameters.Add("#id", DbType.Int32).Value = 2; // delete the book with id = 2
cmdByTitle.Parameters.Add("#title", DbType.String).Value = $"Sample Title #5"; // delete all books having title "Sample Title #5"
cmdByWriter.Parameters.Add("#writer", DbType.String).Value = $"Sample Writer #3"; // delete all books written by "Sample Writer #3"
cn.Open();
cmdById.ExecuteNonQuery();
cmdByTitle.ExecuteNonQuery();
cmdByWriter.ExecuteNonQuery();
cn.Close();
}
}
void ListData(string dbName)
{
string selectCommand = "select * from books";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(selectCommand, cn))
{
cn.Open();
var r = cmd.ExecuteReader();
while (r.Read())
{
Console.WriteLine($"{r["id"]},{r["title"]},{r["writer"]},{r["publisher"]},{r["published_year"]}");
}
cn.Close();
}
}
private void CreateDb(string dbName)
{
if (File.Exists(dbName)) // if it exists, delete and create afresh, just for sampling
{
File.Delete(dbName);
}
string createTable = #"Create Table books (
id int primary key not null,
title varchar(500) not null,
writer varchar(100) not null,
publisher varchar(100) not null,
published_year int not null
)";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(createTable, cn))
{
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
}
private void SeedSampleData(string dbName)
{
string insertCommand = #"insert into books
(id, title, writer, publisher, published_year)
values
(#id, #title, #writer, #publisher, #year);";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(insertCommand, cn))
{
cmd.Parameters.Add("#id", DbType.Int32);
cmd.Parameters.Add("#title", DbType.String);
cmd.Parameters.Add("#writer", DbType.String);
cmd.Parameters.Add("#publisher", DbType.String);
cmd.Parameters.Add("#year", DbType.Int32);
Random r = new Random();
cn.Open();
int id = 1;
using (SQLiteTransaction transaction = cn.BeginTransaction())
{
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"Around the World in Eighty Days";
cmd.Parameters["#writer"].Value = $"Jules Verne";
cmd.Parameters["#publisher"].Value = $"Le Temps, Pierre-Jules Hetzel";
cmd.Parameters["#year"].Value = 1873;
cmd.ExecuteNonQuery();
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"A Tale of Two Cities";
cmd.Parameters["#writer"].Value = $"Charles Dickens";
cmd.Parameters["#publisher"].Value = $"Chapman & Hall";
cmd.Parameters["#year"].Value = 1859;
cmd.ExecuteNonQuery();
// add dummy 10 more rows
for (int i = 0; i < 10; i++)
{
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"Sample Title #{i}";
cmd.Parameters["#writer"].Value = $"Sample Writer #{r.Next(1, 5)}";
cmd.Parameters["#publisher"].Value = $"Sample Publisher #{i}";
cmd.Parameters["#year"].Value = r.Next(1980, 2022);
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
// databases generally use some indexes
new SQLiteCommand(#"Create Index if not exists ixId on books (id);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixTitle on books (title);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixWriter on books (writer);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixPublisher on books (publisher);", cn).ExecuteNonQuery();
cn.Close();
}
}
LiteDb sample:
private static readonly string dataFile = #"d:\temp\books.litedb";
void Main()
{
//CreateDb(dataFile); // this step is not needed with LiteDB
// instead we just simply delete the datafile if it exists
// for starting afresh
// if it exists, delete and create afresh, just for sampling
// so you can run this same sample over and over if you wish
if (File.Exists(dataFile))
{
File.Delete(dataFile);
}
SeedSampleData(dataFile);
// List the current data
Console.WriteLine("Current Data");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
DeleteSampleRow(dataFile);
// List the current data
Console.WriteLine("After deleting");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
// by ID
bookCollection.Delete(2);
// by Title
bookCollection.DeleteMany(c => c.Title == "Sample Title #5");
// by Writer
bookCollection.DeleteMany(c => c.Writer == "Sample Writer #3");
}
}
void ListData(string dbName)
{
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
foreach (var book in bookCollection.FindAll())
{
Console.WriteLine($"{book.Id},{book.Title},{book.Writer},{book.Publisher},{book.Published_year}");
}
}
}
private void SeedSampleData(string dbName)
{
Random r = new Random();
var books = new List<Book> {
new Book {Title="Around the World in Eighty Days",Writer = "Jules Verne",Publisher = "Le Temps, Pierre-Jules Hetzel",Published_year= 1873},
new Book {Title="A Tale of Two Cities",Writer = "Charles Dickens",Publisher = "Chapman & Hall",Published_year= 1859},
};
// add dummy 10 more rows
books.AddRange(Enumerable.Range(0, 10).Select(i => new Book
{
Title = $"Sample Title #{i}",
Writer = $"Sample Writer #{r.Next(1, 5)}",
Publisher = $"Sample Publisher #{i}",
Published_year = r.Next(1980, 2022)
}));
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
bookCollection.InsertBulk(books);
// databases generally use some indexes
// create the same indexes that we created in SQLite sample
bookCollection.EnsureIndex(c => c.Id);
bookCollection.EnsureIndex(c => c.Title);
bookCollection.EnsureIndex(c => c.Writer);
bookCollection.EnsureIndex(c => c.Publisher);
}
}
public class Book
{
public int Id {get;set;}
public string Title {get;set;}
public string Writer {get;set;}
public string Publisher {get;set;}
public int Published_year {get;set;}
}
welcome to SO. I'm going to assume you've got a reason for keeping the data in a text file. As several answers have suggested if you need it in a text file the easiest thing to do is to simply create a new file with the lines you want.
One way to do that is to make use of a interator function to filter the lines. This lets you easily use the .NET File class to do the rest - creating the new file and removing the old if you want to. Often keeping the old file and archiving it can be useful too but anyway, here's a way to filter the lines.
static void Main(string[] _)
{
var filteredLines = FilterOnID(File.ReadAllLines("datafile.txt"), "2");
File.WriteAllLines("updated.datafile.txt", filteredLines);
// rename if necessary
File.Delete("datafile.txt");
File.Move("updated.datafile.txt", "datafile.txt");
}
static IEnumerable<string> FilterOnID(IEnumerable<string> lines, string id)
{
foreach (var line in lines)
{
var fields = line.Split(';');
if (fields.Length != 0 || !string.IsNullOrEmpty(fields[0]))
{
if (id == fields[0])
continue;
}
yield return line;
}
}
To test I added simple file like so:
1;field1;field2;field3
2;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3
And after running you get this:
1;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3
When you put books into a list from a file, you can search the book for remove from BooksList.
Delete it and save BooksList into a file.
var removeBook = BookList.FirstOrDefault(book => book.id == removeId);
if (removeBook != null)
{
BookList.Remove(removeBook);
}
var booksAsString = BookList.Select(book => $"{book.id};{book.title};{book.writer};{book.publisher};{book.published_year}");
File.WriteAllLines("konyvek.txt", booksAsString, Encoding.UTF8);

Export data to text file with fixed length records

I am using MYSQL, WinForms and c#
I have a requirement when I next to get data from my database and export to a text file but each entry needs to be a specific length regardless of the information the database brings in -where data is less than total amount required, empty spaces are to follow it.
E.g First Name could bring in Sam or Samatha but I need to export to be 8 characters in length
"Sam " (8 total characters)
"Samatha "(8 total characters)
this is the function that I am using - any help would be greatly appreciated
public MySqlDataReader RunQueryTextFile(string query, string s1,string pathName)
{
MySqlConnection conDataBase = new MySqlConnection(connString);
MySqlCommand cmdDatabase = new MySqlCommand(query, conDataBase);
MySqlDataReader myReader;
StreamWriter sw = new StreamWriter(#pathName);
conDataBase.Open();
myReader = cmdDatabase.ExecuteReader();
while (myReader.Read())
{
sw.WriteLine (myReader[s1].ToString());
}
sw.Close();
conDataBase.Close();
return myReader;
}
use PadRight
int totalLength = 8;
myString.PadRight(totalLength, " ").
You trying to write procedural code. Think objects. I would do something like this
// create enum - how to pad
public enum PadStyle
{
Right
Left
}
// create attribute class
public class PaddingAttribute : Attribute
{
public int TotalLen {get;set;}
public PadStyle Style {get;set;}
public char PadChar {get;set;}
public int Order {get;set;}
}
// Create class record base - this one knows how to get properties, read attributes and pad values into one string
public class RecordBase
{
protected string CreateRecordProtected()
{
// here you
// 1 - use reflection to get properties
// 2 - use reflection to read PaddingAttribute from properties
// 3 - pad property values using data in PaddingAttribute
// 4 - concatenate record
// something like
var result = o.GetPropeties().Where(...).Select(...padding logic...);
return string.Join("", result);
// padding logic = string.PadRight or string.PadLeft
}
}
Read here how to deal with attribute classes
// Create class record - this one corresponds to your DB
public class Record : RecordBase
{
private const string _fn_FiedName = "firstName";
private const string _ln_FiedName = "lastName";
public Record(IDataReader r)
{
FirstName = r[_fn_FiedName];
LastName = r[_ln_FiedName];
}
[Padding(TotalLen = 15, Style = PadStyle.Right, PadChar = " ", Order = 1)]
public string FirstName{get;set;}
[Padding(TotalLen = 20, Style = PadStyle.Right, PadChar = " ", Order = 2)]
public string LastName {get;set;}
public override string ToString()
{
return base.CreateRecordProtected();
}
And now, your code with some mods
var recList = new List<Record>();
using (var conn = new MySqlConnection(connString))
{
using (var cmd = new MySqlCommand(query, conn))
{
conn.Open();
using (var reader = cmdDatabase.ExecuteReader())
{
while (reader.Read())
{
recList.Add(new Record(reader));
}
}
}
conn.Close();
}
if (!recList.Any()) // System.Linq
return;
IEnumerable<string> textFileRecords = recList.Select(x => x.ToString());
File.WriteAllLines(pathName, textFileRecords); // System.IO
Now, you have some decent reusable code.
NOTE: if data amount is huge, you can append right in while
var rec = new Record(reader);
rec.ToString() // append this

Converting types C#

I am having difficulty with the following method:
public override List<Team> Search(Dictionary<string, string> prms,
int pageSize, int page, out int results)
{
List<Team> t = null;
//Team t = null;
var tresults = new List<Team>();
using (SqlConnection conn = DB.GetSqlConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"SearchForTeam";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
foreach (var key in prms.Keys)
{
cmd.Parameters.Add(key, prms[key]);
}
SqlDataReader reader
= cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
var temp = Load(reader);
if (t == null)
{
t = temp;
}
else
{
t.CityHistory.Add(temp.CityHistory[0]);
}
}
}
}
results = 0;
return t;
}
The error lies mainly with the if and else statement where the temp in the if block is claiming that it "cannot implicitly convert type DataLayer.Team to System.Collections.GenericList"
EDIT:
Here is my load method:
public Team Load(SqlDataReader reader)
{
var team = new Team()
{
TeamID = Int32.Parse(reader["TeamID"].ToString()),
TeamName = reader["TeamName"].ToString()
};
team.CityHistory.Add(
new TeamCity(
Int32.Parse(reader["TeamCitiesID"].ToString()),
team.TeamID,
Int32.Parse(reader["CityID"].ToString()),
reader["CityName"].ToString(),
Int32.Parse(reader["YearStart"].ToString()),
Int32.Parse(reader["YearEnd"].ToString())
)
);
return team;
}
t is defined as List<Team>, yet you later say t.CityHistory. CityHistory is clearly not a property of List<>. I'd guess it's a property of Team, but since you never show us that we can't say.
Show us the definition of Team and the method signature of Load() and perhaps we can give an answer.
UPDATE (from OP's update)
Now, I'm going to assume that you are getting multiple rows, one for each City, with the team info repeating. So, which you want is:
var temp = Load(reader);
// remove t definition above
var t = tresults.FirstOrDefault(team=> team.TeamId == temp.TeamId);
if (t == null)
{
t = temp;
tresults.Add(t);
}
else
t.CityHistory.Add(temp.CityHistory[0]);
(Updated again, based on Steve's comment)
You have to wrap temp into a List first to assign it to t:
if (t == null) {
t = new List<Team>() { temp }
} else {
t.add(temp);
}
VERY LONG COMMENT:
Consider the following refactoring of your method:
private IEnumerable<Team> LoadTeams()
{
using (SqlConnection conn = DB.GetSqlConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"SearchForTeam";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
foreach (var key in prms.Keys)
{
cmd.Parameters.Add(key, prms[key]);
}
SqlDataReader reader
= cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
yield return Load(reader);
}
}
}
}
public override List<Team> Search(Dictionary<string, string> prms,
int pageSize, int page, out int results)
{
List<Team> searchResult = new List<Team>;
//Team t = null;
var tresults = new List<Team>();
foreach(Team team in LoadTeams())
{
if (team .....)
searchResult.Add(team);
}
results = 0;
return searchResult;
}
Take into account this NEEDED initialization:
List<Team> searchResult = new List<Team>;
And ask yourself: What should the following excerpt do? :
if (team .....)
searchResult.Add(team);
P.S.: Also the line
results = 0;
should probably look like:
results = searchResult.Count;
I'm having to guess a little here, but I assume that your Load() method is returning a data type 'DataLayer.Team', which sounds like its one Team, and you're trying to assign it to 't', which is a list of teams.
Try:
t.Add(temp)
or
t.Add(temp as Team).
It doesn't help that you're using 'var' declarations all the time.

How to save values in database using Sqlite?

I am trying to do a tracker application in wp8. I want to save the values in database using Sqlite. All the values r working (time, distance and pace). After pressing Stop button, I want the values to be posted in a table view using Sqlite as u can see in the second screen. Any good suggestions or links are appreciated. Thank u.
Try this nokia developer site here.
Gives you a small tutorial how to use sqlite on windows phones.
This piece of code gives you the answer?
private void Insert_Click_1(object sender, RoutedEventArgs e)
{
// Create a new task.
Task task = new Task()
{
Title = TitleField.Text,
Text = TextField.Text,
CreationDate = DateTime.Now
};
/// Insert the new task in the Task table.
dbConn.Insert(task);
/// Retrieve the task list from the database.
List<Task> retrievedTasks = dbConn.Table<Task>().ToList<Task>();
/// Clear the list box that will show all the tasks.
TaskListBox.Items.Clear();
foreach (var t in retrievedTasks)
{
TaskListBox.Items.Add(t);
}
}
hm, i see this is a retrieval piece of code. Maybee this site helps you further.
The following example is an insert:
public void Initialize()
{
using ( var db = new SQLite.SQLiteConnection( _dbPath ) )
{
db.CreateTable<Customer>();
//Note: This is a simplistic initialization scenario
if ( db.ExecuteScalar<int>(
"select count(1) from Customer" ) == 0 )
{
db.RunInTransaction( () =>
{
db.Insert( new Customer() {
FirstName = "Jon", LastName = "Galloway" } );
db.Insert( new Customer() {
FirstName = "Jesse", LastName = "Liberty" } );
} );
}
else
{
Load();
}
}
}
I'm assuming your table is.
public class HistoryTable
{
public string date { get; set; }
public string time { get; set; }
public double pace { get; set; }
public double distance { get; set; }
}
Insert values using this statement.
string date = DateTime.Now.ToShortDateString();
string time = DateTime.Now.ToShortTimeString();
double pace = 16;
double distance = 4;
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
conn.Execute("Insert into HistoryTable values(?,?,?,?)", date, time, pace, distance);
Fetch your data as below statement, I'm assuming that you know how to bind the data in listbox if there is need. I'm taking the values in textbox.
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
var result = conn.Table<HistoryTable>();
foreach (var val in result)
{
TimeSpan pace = TimeSpan.FromMinutes(val.pace);
TextBoxDate.Text = val.date;
TextBoxTime.Text = val.time;
TextBoxPace.Text = pace.Minutes + ":" + pace.Seconds;
TextBoxDistance.Text = val.distance + " Km";
}
The reason why I used Pace as double because I can use this double value as total minutes and can be changed as timespan(in minutes and seconds). For any other queries you can ask any time.
To get exactly the same format as you ask in your question you should use like this also.
string date = string.Format("{0:00}.{1:00}.{2:00}", DateTime.Now.Day, DateTime.Now.Month, DateTime.Now.Year);
string time = DateTime.Now.ToString("HH:mm:ss");
double pace = 16.5;
double distance = 4;
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
conn.Execute("Insert into HistoryTable values(?,?,?,?)", date, time, pace, distance);
At the time of fetching info, please change the above steps by this.
TextBoxDistance.Text = string.Format("{0:0.00}Km", val.distance);

SQLite reports insert query error even though the row inserted

I am using http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki version 1.0.82.0
When I insert a row like so:
"INSERT INTO [testtable] (col1,col2) values ('','')"
I always get a result of 1 from SQLiteCommand.ExecuteNonQuery(); where it should be returning 0 (OK) or 101 (DONE). I know the row is getting inserted just fine because the auto increment value increases each time I run the method.
The class
readonly object _threadlock = new object();
readonly string _file;
public CSQLite(string file)
{
_file = file;
}
private SQLiteConnection _sqlCon;
private SQLiteCommand _sqlCmd;
private SQLiteDataAdapter _db;
private void SetConnection()
{
lock (_threadlock)
{
_sqlCon = new SQLiteConnection(String.Format("Data Source={0};Version=3;New=False;Compress=True;", _file));
}
}
public int SimpleInsertAndGetlastID(out int id)
{
lock (_threadlock)
{
SetConnection();
_sqlCon.Open();
//Execute
_sqlCmd = _sqlCon.CreateCommand();
_sqlCmd.CommandText = "INSERT INTO [testtable] (col1,col2) values ('','')";
var res = _sqlCmd.ExecuteNonQuery();
//Get id
_db = new SQLiteDataAdapter("select last_insert_rowid();", _sqlCon);
var ds = new DataSet();
_db.Fill(ds);
DataTable dt = ds.Tables[0];
var val = dt.Rows[0][0].ToString();
Int32.TryParse(val, out id);
_sqlCon.Close();
return res;
}
}
The Test:
/// <summary>
///A test for SimpleInsertAndGetlastID
///</summary>
[TestMethod()]
public void SimpleInsertAndGetlastIDTest()
{
var file = "dbs\\test.db";
var target = new CSQLite(file);
var id = -1;
var res = -1;
try
{
res = target.SimpleInsertAndGetlastID(out id);
}
catch (Exception ex){/*Breakpoint*/}
Assert.IsTrue(id > 0); //id gets +1 every time the test is run so the row *is* getting inserted
Assert.IsTrue(res==0||res==101); //Res is always 1 for some reason
}
Table creation (in case that's the problem):
public List<string> Columns { get; set; }
if (!File.Exists(_dbFile))
SQLiteConnection.CreateFile(_dbFile);
var fieldqry = "";
var count = 0;
Columns.ForEach((field) =>
{
count++;
fieldqry += String.Format("[{0}] TEXT NULL", field);
if (count < Columns.Count)
fieldqry += ",";
});
var qry = String.Format("CREATE TABLE IF NOT EXISTS [{0}](" +
"[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
"{1}" +
");", TableName, fieldqry);
var sql = new CSQLite(_dbFile);
var res = sql.Execute(qry);
if(res!=SqLiteErrorCodes.SQLITE_OK)
throw new SqLiteImplementationException("Query failed.");
Where columns is new List<string> { "col1", "col2" } };
Can anyone tell me what I did wrong?
ExecuteNonQuery() does not return a SQLite error code, it returns the number of rows affected by the query. If you are inserting a row, 1 sounds like the expected result if the operation was successful.
The result from ExecuteNonQuery is concidered as "number of rows affected" and not an error code :-)

Categories

Resources