immediate fetching of data from sql database from winforms textbox - c#

I am using Winforms for my application & SQL Server as database.
I want that as soon as any text is typed in a textbox , immediate results are fetched/searched from the SQL SERVER database tables for that supplied text.
For this , i have given the following query:
public partial class Form1 : Form
{
SqlConnection conn = new SqlConnection();
public Form1()
{
conn.ConnectionString = "Trusted_Connection=true";
conn.Open();
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
DataTable dt = null;
SqlCommand cmd = new SqlCommand ("SELECT * FROM items WHERE item_name LIKE'" + textBox1.Text + "%'", conn);
SqlDataReader reader = cmd.ExecuteReader();
dt = new DataTable();
dt.Load(reader);
dataGridView1.DataSource = dt;
}
}
But , as this fetches data every time from the database, so it takes more time, but i want a faster way. so shall i use DATASETS for this purpose, as datasets are used for disconnected environment.
OR
I shall first fetch the whole ITEM table from the database on to a GridView , & display it when the Form is opened.
now, when text is entered in the textbox , then it would not fetch data from the sql database, but would search in the GridView, so would this be faster?
which way would be efficient?
The item table has 3.4 million records.

How big is your items table?
If it's not big, it'll do to just store it in a dataset. Use the same textbox but search in the dataset.
If it's big, I would suggest using a timer. On each textchange, restart the timer of maybe 0.5 seconds. when the timer has elapsed, then only query the database. This prevents multiple queries while the user is typing.
Alternatively, if you could read the whole table and assign it to the AutoCompleteCustomSource:
textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
foreach(DataRow row in dt.Rows)
textBox1.AutoCompleteCustomSource.Add(row["item_name"] as string);

Yes. Using a dataset and searching on it would be much faster. Since you are using WinForms, memory footprint is probably also not an issue unless you you are fetching a huge number of rows from the database.
Also, you should probably not search on every text change, but wait for a small amount of time say 2 seconds during which there are no changes to the textbox and then fetch. Otherwise you would be fetching for any new character entered in the textbox (i think).

Better approach will be using DataSet / DataTable. Read all the data from the Table on the form load and store it in the Form.

Related

Customize gridview c# windows application

My Table has 10 columns fetched from a database, while I need to bind only 4 columns of it to a DataGridView in that 4 column 3 columns from database one of them should add dynamically(This column not in database) extra Windows application c#
string sql1 = "Select * From Table";
SqlConnection connection1 = new SqlConnection(constring);
SqlDataAdapter dataadapter = new SqlDataAdapter(sql1, connection1);
DataSet ds1 = new DataSet();
connection1.Open();
dataadapter.Fill(ds1, Reporttype_tbl1);
connection1.Close();
dataGridView2.DataSource = ds1;
dataGridView2.DataMember = Reporttype_tbl1;
If you need extra processing beyond the data that you have in your table you could:
(Imagine that you want to calculate the revenue, giving the income and expenses of a business)
Use the query itself to do the extra processing for these columns that are not in the database.
For example: SELECT Name, Income, Expenses, Revenue = Income - Expenses FROM TABLE
In this case, the Revenue will be calculated as the query runs.
Use your C# code to do the extra processing and change your datasource to the new one.
For example:
Add a Revenue column to your DataTable
Make the formula for each line
Use the new DataTable as DataSource
In this case, you are adding the data after the query has been completed, using your C# code to the calculations.
Picking the right one depends on whats kind of work you are wanting to do and what you feel more comfortable.

Problem with format in C# with DataGridView and TextBox

I have a problem with format in C#.
I have a DataGridView and a TextBox. In this datagridview, there is a column: the single price (format int).
I want sum every elements of single price's column and insert the result into this textbox, but Visual Studio gives me a problem with format of string input ("Format of input's string is not correct").
this is the code than i used:
int TOT = 0;
for (int i = 0; i < dataGridView3.Rows.Count; i++)
{
TOT = TOT + Convert.ToInt32(dataGridView3.Rows[i].Cells[6].ToString());
}
textBoxTot.Text = Convert.ToString(TOT);
Can you help me with this bad error?
UPDATE:
I think that the problem now is another. I can't find the methods of MySql.Data.MySqlClient library that it can give me the result of query.
MySqlCommand command = new MySqlCommand();
String sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
command.CommandText = sumQuery;
command.Connection = conn.getConnection();
command.Parameters.Add("#prezzo", MySqlDbType.Int32).Value = costo;
conn.openConnection();
conn.closeConnection();
How is the command that give me the result of sumQuery. If i find this command, i can take the result of query and paste in textbox
If your datagridview is showing a datatable (I.e. your data is stored in a datatable) you can add a DataColumn to the datatable whose .Expression property is set to the string "SUM([Price])", where Price is the name of your numeric datatyped column holding the price info.
Now every row in the table will have the sum of the prices (the same value over and over again), so if you want to databind your textBox to this new column then it will always show the sum no matter which row is the current row (because all rows have the same value). It will also auto update without you having to do anything!
And if you're not using databinding to a datatable, I recommend that you do do it, because it represents good MVC practice of keeping your data in one thing (DataTable) and showing it in another (DataGridView) and keeping these concerns separate
It would look something like this, as a quick example:
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Price", typeof(int));
dt.Columns.Add("CalculatedTotal", typeof(int)).Expression = "SUM([Price])";
dt.Rows.Add("Apples", 100);
dt.Rows.Add("Oranges", 200);
BindingSource bs = new BindingSource();
bs.DataSource = dt;
WhateverDataGridView.DataSource = bs;
totalTextBox.DataBindings.Add(new Binding("Text", bs, "CalculatedTotal", true));
Here we have a data model (the datatable) where we keep our data. It has a couple of things we can set directly, and an third column that calculates based on the existing prices in all the table. If you look at this in the datagridview (assuming you have autogeneratecolumns turned on) you'll see that both rows have 300 for the CalculatedTotal, but they have individual amounts for price. There is a device called a BindingSource that sits between the datatable and the UI controls; you don't have to have one but it makes certain things easier regards updating controls when the data changes, and it maintains a concept of "current" - essentially whatever the current row is you're looking at in the datagridview; it all helps to avoid having to ask the DGV for anything - we just let the user type into the DGV, and it shows the data out of the datatable. All our dealings can be with the datatable directly - if you wrote a button to loop through the table and double all the prices, the controls in the UI would just reflect the change automatically. The textbox is connected to the CalculatedValue column via databinding; whatever the current row is, the textbox will show the CalculatedValue. Because the CalculatedValue column has the same value on every row, and they always all update if any price changes, the total textbox will always show the total. Add another textbox bound to Name to see what I mean; as you click around the grid and select different rows to be the "Current" row, the Name will change but the total does not. In truth it is actually changing in the same way that Name is, it's just that because the actual numeric value is the same on every row the contents of the textbox look like they don't change
UPDATE: I think that the problem now is another. I can't find the methods of MySql.Data.MySqlClient library that it can give me the result of query.
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
String sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
command.CommandText = sumQuery;
command.Connection = conn.getConnection();
command.Parameters.Add("#prezzo", MySqlDbType.Int32).Value = costo;
conn.openConnection();
conn.closeConnection();
}
How is the command that give me the result of sumQuery. If i find this command, i can take the result of query and paste in textbox
It is weird that you are first converting to a string and then to an int.
int TOT = 0;
for (int i = 0; i < dataGridView3.Rows.Count; i++)
{
if (!dataGridView3.Rows[i].IsNewRow &&
int.TryParse(dataGridView3.Rows[i].Cells[6].Value.ToString(), out int v))
TOT += v;
}
textBoxTot.Text = TOT.ToString();
EDIT: Edited for your updated question. You shouldn't ask question inside a question buy anyway:
string sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
decimal total = 0M;
using (MySqlConnection cn = new MySqlConnection(" your connection string here "))
using (MySqlCommand cmd = new MySqlCommand(sumQuery, cn))
{
cn.Open();
total = Convert.ToDecimal(cmd.ExecuteScalar());
cn.Close();
}
Console.WriteLine(total);

Excessive Memory Spike On Postback Because Of Datatable

I basically have a listbox that has postcode areas i.e : AE,CW,GU etc etc.
The user selects this and then a postback occurs - an sql statement is builts and a database query operation is performed and the results are returned to a datatable called tempdata.
So far so good. I then need to loop through this datatable and copy the records to my main viewstate datatable which is the datasource for google maps api.
DataTable tempstore = GetData(querystring, "");
//check tempstore has rows otherwise add defaultcust as default otherwise map will be blank
if (tempstore.Rows.Count == 0)
{
tempstore = GetData("WHERE CUSTCODE=='CD344'", "");
infoalert.Visible = true;
infoalert.InnerHtml = "No Results Returned For Selection";
}
foreach (DataRow row in tempstore.Rows)
{
dtpc.ImportRow(row);
dtpc.AcceptChanges();
}
//database command
using (OleDbConnection con = new OleDbConnection(conString))
{
using (OleDbCommand cmd = new OleDbCommand(query))
{
using (OleDbDataAdapter sda = new OleDbDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
sda.Fill(dt5);
}
}
}
So my main datatable can grow and grow as users add more postcodes. However when it gets to around 500 rows or so I get a huge memory spike only on postback and then it settles back down.My ram usage goes from 2gb to 3gb and if even more postcodes is selected it maxes the memory and crashes my pc.
If I remove the:
dtpc.Importrow(row);
the memory spike goes completely, obviously because the main datatable has no rows. I thought you only run into memory issues when you have thousands of rows?
Any help would be much appreciated.
thank you
Do you really need all the rows at once
A DataReader will access a single row at a time and keep you memory to a minimum
DataReader class
If you need all you data at once create a class of strut for the data and hold it in a collection like a List. DataTable is a heavy object.
And if you are measuring memory via Task Manager be aware it is not very accurate.
First off, make sure you're wrapping any SQL execution in the appropriate "using" clauses. This is most likely the cause of your problem.
using (var command = new SqlCommand())
{
// Some code here
}
Like Blam said, DataTable is too heavy for your purposes.
You can convert your data rows into class objects quite easily:
var datasourceList = new List<YourCustomObject>();
foreach (DataRow row in tempstore.Rows)
{
var newMapsObject = new YourCustomObject
{
Value1 = row.Field<String>("Value1ColumnName"),
Value2 = row.Field<String>("Value2ColumnName")
};
datasourceList.Add(newMapsObject);
}
viewStateList.AddRange(datasourceList);
To bind a custom collection to a data display (such as a repeater) you assign the list to the .DataSource property of said display, then call .DataBind(). This will work for most all ASP.NET data display objects.
repeater1.DataSource = viewStateList;
repeater1.DataBind();

Visual C# DataGridView editing; have set all possible edit options; wont edit

I've spent about three weeks trying to find information on this and to no success.
DGV settings related to editing:
readonly is false on DGV
edit mode is keystroke on DGV.
code is here:
http://pastebin.com/eCv3iBcF
However, when trying to edit, nothing happens, the box just turns color from selection; the mouse doesn't change to recognize text, keystrokes do nothing.
What this is supposed to do (at least in my head and according to the tut):
Call the table.
Populate the DGV.
Allow edits on selected field by keystroke.
The problem is that you are setting the datasource of your BindingSource object as the SQLDataReader Object. This is a Read-Only Object.
A DataReader is limited to being read-only and forward-only. That is,
the information retrieved from the database cannot be modified by the
DataReader, nor can the DataReader retrieve records in a random order.
Instead, a DataReader is limited to accessing the records in
sequential order, from the first one to the last one, one record at a
time."
You should use a DataAdpater instead and populate a DataTable.
You then use the DataTable as the Datasource for your BindingSource.
Alternativley, you cans tick with your DataReader like this:-
using(SqlDataReader reader = command.ExecuteReader())
{
DataTable data = new DataTable();
data.Load(reader);
}
Hope this Helps.
Change your coding like below... it will help you...
string query = "SELECT * FROM catalog";
using (MySqlConnection con = new MySqlConnection (connectionString))
{
con.Open();
using (MySqlDataAdapter dataAdapter = new MySqlDataAdapter(query , con))
{
DataSet ds = new DataSet();
dataAdapter.Fill(ds);
dataGridView1.DataSource = ds.Tables[0];
}
con.Close();
}

Single datagridview row updating

Could you help with such problem:
I have two forms, datagridview and SQL database.
On Load event of my first form I'm select some data from my SQL database by using stored procedure(select query).
SqlConnection con = new SqlConnection(constr);
SqlCommand cmd = new SqlCommand("PROC001", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
datagridview.DataSource = dt;
After that, I can filter or sort my datagridview by using dt.DefaultView.Filter = .... and display in my grid only filtered rows.
On CellMouseDoubleClick event my second Form2 appearing. In this form I update some value in database by clicking on Button1 and also after that I want to update my selected datagridview row. My quetions are:
1) How can I update only selected row in datagridview and do not execute stored procedure for all datagridview filling again.
2) My datagridview is already filtered, so if I execute procedure again, my filter has been disapeared. How can I avoid of this?
3) On Form2 I'm updating some database value, that is not included in my "select query" as selected field, but this value is affected on this query. Example:
SELECT Name, SecondName FROM tUsers
WHERE id = (SELECT DISTINCT id FROM tProcedures WHERE Code = 'First')
In my datagridview I can see Name and SecondName, but in Form2 I'm updating Code in tProcedures database table. So after updating I want to see my new data in datagridview, only in selected row and with current filter. I don't want to SELECT all data again to datagridview and broke my filter.
Is it possible to update single row? Could you provide some examples?
Because the DataGridView is using DataBinding, you have to update the underlying data source, in this case the DataTable. See How to: Edit Rows in a DataTable for how to do that.
For the filter issue, you would want to save and restore the filter:
var filter = dt.DefaultView.RowFilter;
UpdateData();
dt.DefaultView.RowFilter = filter;

Categories