0

I would like to ask if it is possible to load a database into an array or list, and then run queries on it? I have the following code.

string cs = "Data Source=dataBase.sqlite;Version=3;";   
SQLiteConnection con;
SQLiteDataAdapter adapt;
DataTable dt;

private void textBox1_TextChanged(object sender, EventArgs e)
{
    con = new SQLiteConnection(cs);
    con.Open();
    adapt = new SQLiteDataAdapter("select * from Table1 where CnName1 like '" + textBox1.Text + "%'", con);
    dt = new DataTable();
    adapt.Fill(dt);
    dataGridView1.DataSource = dt;
    con.Close();
}

This works, but it creates a new dataTable whenever a query is run, the problem code is:

dt = new DataTable(); 

The program is meant to be constantly running, so this is inefficient, since it will eat up a lot of memory. How do I load the database into an object, and then run queries on that object? The table is only meant to have 1 column, and the queries run will only serve as a search function. I want to load the database only once, that is when the program is started, then the connection will be closed, and everything else will be done with the program, not with the database.

Edit: I would like to state for anyone else viewing this question to also view saab669's answer, as it provides useful information as well, however I can not choose two answers.

EInherjar
  • 339
  • 6
  • 17
  • All the objects you are creating under the text changed event will become out of scope and eventually garbage collected once the event is completed so memory shouldn't be an issue. Make sure you dispose of your db objects where possible. No application should be looking to load all the database into memory at once. You need to refactor your code and move your DB stuff into a class that returns you a DataTable so you can act upon that until the text changed fires again for new presumably new data based upon the content of textBox1 – Wheels73 May 02 '17 at 11:42
  • If you want the code to load the data only once, then you shouldn't be interacting with the SQL / database in a `TextChanged` event. Every time you type something in your text box it's going to re-query the database. Instead, you should populate your `DataTable` just like you are -- except in a different event. Perhaps the form load event? Then in your text changed event, [use this MSDN article](https://msdn.microsoft.com/en-us/library/y06xa2h1.aspx) to find rows by column values. – sab669 May 02 '17 at 11:47
  • The database itself is the object you are mention. Simply query it *each* time you need *new* (another) data. Store result of query if you are going to keep use it. In given case you are doing correctly - querying database every time filter is changed (because this will require a new query to run, returning another set of results). Alternatives are: 1) load all records and apply [filter in memory](http://stackoverflow.com/q/13012585/1997232) (only good for small amount of data) 2) organize [paging](http://stackoverflow.com/q/14468586/1997232) or virtualize result. – Sinatr May 02 '17 at 11:57
  • Do you need to load the whole of "Table1" into a datatable.. i.e. with no parameter on the select statement? Where does the text change event come into play? – Wheels73 May 02 '17 at 12:02
  • The database will be roughly 5-15k rows of data. There will only ever be one table with at most 3 columns. I don't want to load it into memory (unless that is necessary to query it). I will try that out, I am fairly new to using SQL w/ winforms and C#. I will read about paging, since I want the user to be able to choose the items stored in the database, and then print (on paper) the selected items. – EInherjar May 02 '17 at 12:10
  • Also, the text change event is because I want the search result to be showed dynamically. – EInherjar May 02 '17 at 12:24

2 Answers2

1

Assuming you have a form containing your text box. Declare a class level variable to store you datatable.

 private DataTable _data;

Create a class to encapsulate your database connection and retrieval of data.

 public class MyDataBaseConnection
    {
        public DataTable ReturnMyData(string valueFromTextBox)
        {

        var cs = "Data Source=dataBase.sqlite;Version=3;";
        SQLiteConnection con;
        SQLiteDataAdapter adapt;
        DataTable dt;  

        try
         {

            con = new SQLiteConnection(cs);
            con.Open();
            adapt = new SQLiteDataAdapter("select * from Table1 where CnName1 like '" + textBox1.Text + "%'", con);
            dt = new DataTable();
            adapt.Fill(dt);
            con.Close();

            return dt;
         }
         catch (Exception ex)
         {
            //Log here.
            throw;
         }
         finally
         {
            con = null;
            adapt = null;
            //Or Dispose. I dont have SQL lite so dont know if they implement IDispose
         }
    }
}

In your textbox change event, call the db code and assign to your class level var

 private void textBox1_TextChanged(object sender, EventArgs e)
 {
      var myDBConnection = new MyDataBaseConnection();
      _data = myDBConnection.ReturnMyData(textBox1.Text);
      dataGridView1.DataSource = null;
      dataGridView1.DataSource = _data;
 }

When the text changed event fires again, the data will be changed in the grid.

Hope that helps.

Wheels73
  • 2,850
  • 1
  • 11
  • 20
  • This does not meet the requirement of the question: `I want to **load the database only once**, that is when the program is started`. Basically he wants to load the data into memory and search it "locally" from the application. – sab669 May 02 '17 at 11:59
  • The select statement takes a parameter. This is not loading the whole DB. I'm about to ask OP if they mean load all of table1 without the text changed in which case the changed event code shouldn't be there. – Wheels73 May 02 '17 at 12:01
  • This helps, but queries will only be ever run in one even, I was mainly worried about performance. So it is not a problem if the program will run for two weeks straight? – EInherjar May 02 '17 at 12:07
  • As long as you dispose of your connection objects correctly, then it should be fine. – Wheels73 May 02 '17 at 12:09
  • Would you please edit the answer to display how the code in the TextChanged event should look like? I did as you asnwered and put all the code from the previous TextChanged event in the Form1_Load event, so the data from the database is now in the dataGridView1. I did not encapsulate it, as I would like to know if it's possible to search the datagridview without creating tons of new objects everytime a search is performed, and if the search query is empty, simply display the original database table. Thank you for the reply – EInherjar May 02 '17 at 13:01
  • Hello... Sorry for the confusion. If you are querying the database each time the Text_Changed fires, i.e. you are not loading the whole db at once, then the code is fine as it is! Your main concern was with memory.. which wont be a problem. – Wheels73 May 02 '17 at 13:09
  • I'm going to edit my answer based on the fact that you "Do not" want to load all 15K rows at once. – Wheels73 May 02 '17 at 13:18
  • How are you sure that the memory won't be a problem? I tried running a while(true) loop on the event, with the text always changing, and I used textBox1.Dispose(); at the end of the loop, but the memory always kept on growing. @wheels73 Ideally all the 15k rows will only be loaded once, that is when the program itself is started. – EInherjar May 02 '17 at 13:29
  • Its not the textbox that's the issue. Disposing of the texbox wont achieve anything, its just a single control to enter the search criteria into. As long as you clean up your database connections I can't see any scope for memory increases aside from you returning a larger data set to bind to the grid with given a certain search text. – Wheels73 May 02 '17 at 13:39
  • @wheels73 Thank you for the answer, one last thing; when I paste the code into my program, it doesn't seem to be working, do I need to define con and adapt again? They are currently defined right under the public partial class Form1 : Form class. Where exactly should I put the code? – EInherjar May 02 '17 at 14:00
  • No problem. If you've kept the db conn code inside the text changed event and used my try catch syntax then declare them above the "try". Perhaps edit your post so I can see where you are at now. There's been a lot of things said! :) – Wheels73 May 02 '17 at 14:07
  • I have not edited anything yet, except for pasting in the code you provided. – EInherjar May 02 '17 at 14:09
  • Ok.. solution updated with declarations for con and adapt. – Wheels73 May 02 '17 at 14:14
0

My response is too long for a comment, but I want to reply to a few things you said:

but it creates a new dataTable whenever a query is run, the problem code is dt = new DataTable(); The program is meant to be constantly running, so this is inefficient, since it will eat up a lot of memory.

Sort of. You only ever have one DataTable object. It's just that every time that event fires, it's re-querying the database. So you're not consuming an excessive amount of memory (unless your DB is huge!), or at least not in the way you think you are.

How do I load the database into an object, and then run queries on that object?

As I mentioned in my comment, you're doing that already. Just in a less-than-ideal event handler. Depending on how you want the application to behave, you should move this to the form load event as defined here. Or perhaps it might make more sense to have a button with a click event. The form load could add a long delay to your application's start up and the user might think it froze, depending on how long it takes you to fetch all the records from the DB.

Also, from the code snippet you provide, am I correct in assuming you define DataTable dt; outside of the event handler, on the class level?

Anyways, per the MSDN article I linked in my comment, once you have a DataTable populated then you can simply do things like this:

DataRow[] foundRows;
foundRows = dataSet1.Tables["TableName"].Select("ColumnName LIKE 'your_search_value%'");

Lastly, and I cannot stress this enough about the code you provided: DO NOT concatenate strings for a query which will execute against a database. I don't care if it's for a homework assignment or a little tool only you will use, there's no excuse for not taking the extra time to learn how to do it the right way. There's no reason to build a bad habbit, as this is susceptible to SQL injection which is comically easy to protect against. You absolutely should spend the 30 minutes to learn about parameterized queries.

sab669
  • 3,984
  • 8
  • 38
  • 75
  • Okay, so if I load the database via button or on form load (5-15k rows), then display the results in a datagridview, I can use the textbox to make queries on the datagridview, not on the database itself? Thank you for the lengthy reply. – EInherjar May 02 '17 at 12:18
  • @EInherjar If you want to manipulate the `DataGridView` directly rather than your `DataTable` (which is SEPARATE from the database) then I would direct you to this question which goes over filtering a `DataGridView`: http://stackoverflow.com/questions/18253624/searching-in-datagridview-and-filtering-it – sab669 May 02 '17 at 12:23
  • The BindingSource.Filter does not let me use it if I declare it on a class level, and I would like to avoid declaring it locally, since that would mean I would create a new instance everytime the textChanged event is run. – EInherjar May 02 '17 at 13:04
  • @EInherjar There is no harm in declaring it within the scope of the event. – sab669 May 02 '17 at 13:58