I have this sub in one form of my program to calculate and display a total profit from my database:
Public Sub Profit()
Try
Using connection As New OleDbConnection(connectionstring)
connection.Open()
Dim Command As New OleDbCommand("SELECT SUM(Price) FROM ItemsSold", connection)
Dim command2 As New OleDbCommand("SELECT SUM(TotalCost) FROM Inventory", connection)
Dim Turnover As Double = Convert.ToDouble(Command.ExecuteScalar())
Dim Cost As Double = Convert.ToDouble(command2.ExecuteScalar())
Dim TotalProfit As Double = Turnover - Cost
lblProfit.Text = "Profit: £" & TotalProfit
lblProfit2.Text = "Profit: £" & TotalProfit
lblProfit3.Text = "Profit: £" & TotalProfit
lblProfit4.Text = "Profit: £" & TotalProfit
connection.Close()
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Also, in a different form of the program, I am updating a database table with a value provided by the user. What I want to do, is add this input value to this profit that is displayed in the label in the Profit() sub.
How can I go about doing this? Would I need to somehow make this profit variable a public shared variable to be able to access it in my other form? Is there another way to just increase this value and display the new increased variable?
How does shared variable supposed to help you?
you should to split functions which works with data and which works with form. In other words, when you need display data, you call GetProfit, and when you need to update profit you call SetProfit(ByVal profit As Double). And then use them in your project.
Based on your code, I suppose it should looks something like that
Public Sub GetProfit()
Dim profit As Double
Try
Using connection As New OleDbConnection(connectionstring)
connection.Open()
Dim Command As New OleDbCommand("SELECT SUM(Price) FROM ItemsSold", connection)
Dim command2 As New OleDbCommand("SELECT SUM(TotalCost) FROM Inventory", connection)
Dim Turnover As Double = Convert.ToDouble(Command.ExecuteScalar())
Dim Cost As Double = Convert.ToDouble(command2.ExecuteScalar())
Dim TotalProfit As Double = Turnover - Cost
profit = TotalProfit
connection.Close()
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
' Return the result
return profit
End Sub
And for update
Public Sub SetProfit(ByVal newProfit As Double)
Try
Using connection As New OleDbConnection(connectionstring)
connection.Open()
' Commands for updating data (just for example)
Dim Command As New OleDbCommand("UPDATE ItemsSold SET Price = " & newProfit)
connection.Close()
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
And then you when you dealing with your form you just do like this
Private Sub Form1_Load()
Label1.Text = GetProfit()
Label2.Text = GetProfit()
End Sub
When you need to update
Private Sub Button1_Click()
SetProfile(GetProfit() + 5)
End
It is not exactly production code, just for an example
Related
Im making a POS style system and this is my code for storing the data for the active order and also inputting the data into the database. This should all work however I get an error on the cmd.ExecuteNonQuery() in the "Push" IF statement.
It gives me an SQLite error 20: Datatype mismatch.
And idea why?
TIA
Imports Microsoft.Data.Sqlite
Public Class CurrentOrder
Public Shared OrderID As Integer
Public Shared Items As New ArrayList
Public Shared ItemsString As String
Public Shared CustName As String
Public Shared Table As Integer
Public Shared Cost As Double 'How much the restaurant will have to pay to make the meal
Public Shared Price As Double 'How much the customer will pay for this order
Public Shared Sub Database(ByVal Mode As String)
Dim Connection As New SqliteConnection("Data Source = Database.db")
Dim SQLcommand As String
Dim CMD As New SqliteCommand
'ItemsString = ""
If Items.Count = 0 Then
MessageBox.Show("Please add items to order", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End If
If Mode = "Push" Then
For i = 0 To Items.Count - 1
ItemsString = ItemsString + Items(i) 'Concatatanation to take a list to a string
Next
Order.Label3.Text = ItemsString
SQLcommand = "INSERT INTO Orders VALUES ('#OrderID', '#ItemsString', '#CustName', '#Table', '#Cost', '#Price')" 'SQL Push Statement
Try
CMD.Connection = Connection
Connection.Open()
CMD.CommandText = SQLcommand
CMD.ExecuteNonQuery() 'Error 20: Datatype mismatch
Connection.Close()
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
ElseIf Mode = "Pull" Then
SQLcommand = ("SELECT * FROM Orders WHERE OrderID = " & OrderID) 'SQL Pull Statement
Try
CMD.Connection = Connection
Connection.Open()
CMD.CommandText = SQLcommand
Dim reader As SqliteDataReader = CMD.ExecuteReader()
While reader.Read()
Order.Label3.Text = reader("ItemID") & ", " & reader("Name") & ", " & reader("Price")
End While
reader.Close()
Connection.Close()
Catch e As Exception
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End If
End Sub
End Class
When dealing with money it is better to use Decimal rather than Double.
I don't know why this class has all these Shared items. It would probably be better to just create an instance in the calling code.
ArrayList should not be used in new code. See if List(Of T) will fill the bill.
Fields are not normally Public in Classes. You want a Property to be Public.
Classes like this have no user interface. You don't want to show message boxes from your class. Show the message boxes from you User Interface code in the Form Class.
Do not store several items in a single field in the database. You should have an Orders table with a primary key OrderID and an OrderDetails table for the items.
Your for loop just lumps all the items together. You will not be able to pull them apart again without any delimiter. Also, the concatenation operator in vb.net is &.
For you insert statement it is usually best to list the field names and then the values. I don't know why you're enclosed the parameter names in single quotes.
You can pass the CommandText and the Connection directly to the constructor of the Command.
You have referenced several parameters in your insert but you never added them to the parameters collection or set a value for them.
Apparently, you are providing a unique value for OrderID and this is not an auto number field.
Use a Sub New to put your order object in a stable state ready to have its data added to the database.
Public Class CurrentOrder
Public Property OrderID As Integer
Public Property Items As New List(Of String)
Public Property CustName As String
Public Property Table As Integer
Public Property Cost As Decimal 'How much the restaurant will have to pay to make the meal
Public Property Price As Decimal 'How much the customer will pay for this order
Public Sub New(ID As Integer, Itms As List(Of String), Name As String, TBL As Integer, Cst As Decimal, Prc As Decimal)
OrderID = ID
Items = Itms
CustName = Name
Table = TBL
Cost = Cst
Price = Prc
End Sub
End Class
The CurrentOrder class is completely separate from the DataAccess class.
Public Class DataAccess
Private ConStr As String = "Data Source = Database.db"
Public Sub SaveOrderAndDetails(O As CurrentOrder)
Dim OrderInsert = "INSERT INTO Orders (OrderID, CustName, Table, Cost, Price) VALUES (#OrderID, #CustName, #Table, #Cost, #Price)"
Using Connection As New SQLiteConnection(ConStr)
Using CMD As New SQLiteCommand(OrderInsert, Connection)
CMD.Parameters.Add("#OrderID", DbType.Int32).Value = O.OrderID
CMD.Parameters.Add("#CustName", DbType.String).Value = O.CustName
CMD.Parameters.Add("#Table", DbType.Int32).Value = O.Table
CMD.Parameters.Add("#Cost", DbType.Decimal).Value = O.Cost
CMD.Parameters.Add("#Price", DbType.Decimal).Value = O.Price
Connection.Open()
CMD.ExecuteNonQuery()
End Using
Dim DetailsInsert = "Insert Into OrderDetails (OrderID, Item) Values (#OrderId, #Item)"
Using cmd As New SQLiteCommand(DetailsInsert, Connection)
cmd.Parameters.Add("#OrderId", DbType.Int32).Value = O.OrderID
cmd.Parameters.Add("#Item", DbType.String)
For Each s In O.Items
cmd.Parameters("#Item").Value = s
cmd.ExecuteNonQuery()
Next
End Using
End Using
End Sub
Public Function GetOrderByID(id As Integer) As DataTable
Dim dt As New DataTable
Dim SqlCommand = "SELECT * FROM Orders WHERE OrderID = #ID"
Using cn As New SQLiteConnection(ConStr),
cmd As New SQLiteCommand(SqlCommand, cn)
cmd.Parameters.Add("#ID", DbType.Int32).Value = id
cn.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
Public Function GetOrderDetailByID(id As Integer) As DataTable
Dim dt As New DataTable
Dim sql = "Select Item From OrderDetails Where OrderID = #ID"
Using cn As New SQLiteConnection(ConStr),
cmd As New SQLiteCommand(sql, cn)
cmd.Parameters.Add("#ID", DbType.Int32).Value = id
cn.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
End Class
Finally, the usage of the classes in the user interface, in this case a windows form.
Private Sub Button1_Click() Handles Button1.Click
Dim lst As New List(Of String)
lst.Add("Bacon")
lst.Add("Eggs")
Dim newOrder As New CurrentOrder(7, lst, "John", 4, 12.03D, 22D)
Dim DA As New DataAccess
DA.SaveOrderAndDetails(newOrder)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim DA As New DataAccess
Dim dt = DA.GetOrderByID(7)
'Fill various text boxes with the DataTable fields
Dim dt2 = DA.GetOrderDetailByID(7)
DataGridView1.DataSource = dt2
End Sub
In the table, you have a different data type and you are sending a different datatype. For Example, In the table, you use Int and you pass decimal.
For all who assist with this, thank you. My goal is to get the duration of chat conversations for my employees and then calculate their year average. The data is being inserted into a database as short text in the format of (mm:ss or 18:30). I have tried converting and parsing the data several different ways and I have looked through numerous explanations and solutions on SO.com so far with nothing working the way I would like. The below is my display procedure to bring the data into the form. I know I could have done this in an easier way, but I am fairly new to VB.net and coding in general.
Private Sub DisplayJanMetrics()
'Open a connection to the database and then assign the values from the appropriate metric columns to the appropriate labels.
Dim str As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\CoachingDB.accdb"
Dim conn As New OleDbConnection(str)
Try
conn.Open()
Dim sql As String = "SELECT Duration, CSAT, Away, Quality, Development FROM January WHERE Employee =" & "'" & cmbEmployee.SelectedItem.ToString & "'"
Dim cmd As New OleDbCommand(sql, conn)
Dim myReader As OleDbDataReader = cmd.ExecuteReader()
While myReader.Read
lblJanDuration.Text = myReader("Duration").ToString
lblJanCSAT.Text = myReader("CSAT").ToString
lblJanAway.Text = myReader("Away").ToString
lblJanQual.Text = myReader("Quality").ToString
lblJanDev.Text = myReader("Development").ToString
End While
Catch ex As OleDbException
MsgBox(ex.ToString)
Finally
conn.Close()
End Try
End Sub
Once the data has been loaded to the correct labels I have a button and click event to calculate the average from the labels - the other ones I was able to do easily because I could parse them to doubles and then do the calculation from there. Here is an image of what the form looks like, I think it will help all of you get an idea of what I am trying to accomplish.
This is what the form layout looks like
It is a good idea to separate your user interface code from your database code. You can use the same data retrieval function for any month by passing the month as TableName.
Database objects Connection, Command, and DataReader all need to be disposed and closed so they are placed in Using blocks. You don't want to hold the connection open while you update the user interface. Just return a DataTable and update the UI with that.
CalculateAverage first creates an array of the labels you want to include in the average. You can include all 12 but the average will not include empty labels. (IsNullOrEmpty)
Separate the string into minutes and seconds. Get the total seconds and add to the list.
Get the average number of seconds by calling Average on List(Of Integer).
Finally, turn the average seconds back into minutes and seconds and format a string to display.
Private Sub DisplayJanMetrics()
Dim dt As DataTable
Try
dt = GetEmployeeData(cmbEmployee.SelectedItem.ToString, "January")
Catch ex As Exception
MsgBox(ex.Message)
Exit Sub
End Try
If dt.Rows.Count > 0 Then
lblJanDuration.Text = dt(0)("Duration").ToString
lblJanCSAT.Text = dt(0)("CSAT").ToString
lblJanAway.Text = dt(0)("Away").ToString
lblJanQual.Text = dt(0)("Quality").ToString
lblJanDev.Text = dt(0)("Development").ToString
End If
End Sub
Private Function GetEmployeeData(Employee As String, TableName As String) As DataTable
Dim str As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\CoachingDB.accdb"
Dim sql As String = $"SELECT Duration, CSAT, Away, Quality, Development FROM {TableName} WHERE Employee = #Employee;"
Dim dt As New DataTable
Using conn As New OleDbConnection(str),
cmd As New OleDbCommand(sql, conn)
cmd.Parameters.Add("#Employee", OleDbType.VarChar).Value = Employee
conn.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
Private Function CalculateAverage() As String
Dim lst As New List(Of Integer)
Dim labels() = {Label1, Label2, Label3, Label4} 'an array of 1 thru 12
For Each label In labels
If Not String.IsNullOrEmpty(label.Text) Then
Dim fields = label.Text.Split(":"c)
Dim mins = CInt(fields(0))
Dim secs = CInt(fields(1))
lst.Add(mins * 60 + secs)
End If
Next
Dim avg = lst.Average
Dim avgMins = Math.Truncate(avg / 60)
Dim remainderSec = avg - avgMins * 60
Return $"{avgMins}:{remainderSec}"
End Function
Im trying to use report viewer to print receipt with pos system but i get always the first order printing details not the current order details
i think the problem with PrintPreview code section where i can put the code exactly
Sub Savepayment()
Try
Dim sdate As String = Now.ToString("yyyy-MM-dd")
Dim stime As String = Now.ToString("hh:mm:ss")
cn.Open()
cm = New SqlCommand("Insert into TblSales(transno,total,sdate,stime,cashier,discount,totalbill) values (#transno,#total,#sdate,#stime,#cashier,#discount,#totalbill) ", cn)
With cm
.Parameters.AddWithValue("#transno", FrmPos.LblTransNo.Text)
.Parameters.AddWithValue("#total", CDbl(TxtBill.Text))
.Parameters.AddWithValue("#sdate", sdate)
.Parameters.AddWithValue("#stime", stime)
.Parameters.AddWithValue("#cashier", str_user)
.Parameters.AddWithValue("#Discount", CDbl(TxtDiscount.Text))
.Parameters.AddWithValue("#totalbill", CDbl(TxtTotal.Text))
'.Parameters.AddWithValue("#Cash", CDbl(TxtCash.Text))
'.Parameters.AddWithValue("#Change", CDbl(TxtChange.Text))
.ExecuteNonQuery()
End With
cn.Close()
cn.Open()
cm = New SqlCommand("Update tblcart set status='Completed' Where transno like '" & FrmPos.LblTransNo.Text & "'", cn)
cm.ExecuteNonQuery()
cn.Close()
MsgBox("Payment successfully Saved!", vbInformation)
With FrmReceipt
.PrintPreview("Select c.id, p.Description, c.price, c.qty, c.total From tblcart as c inner join tblproduct as p on c.pid=p.id where c.status like 'Completed' and TransNo like '" & FrmPos.LblTransNo.Text & "'")
.ShowDialog()
End With
With FrmPos
.LblTransNo.Text = .GetTransNo()
.loadcart()
End With
Me.Dispose()
Catch ex As Exception
cn.Close()
MsgBox(ex.Message, vbCritical)
End Try
End Sub
Your connection should always be declared in the method where is it used with a Using...End Using block. Connection and commands need to be disposed (have their Dispose method called) to release unmanaged resources. Using blocks handle this for us even if there is an error. It also closes the connection.
Don't Open the connection until directly before the Execute... .
Don't use AddWithValue with Sql Server. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
You need to disconnect your database code from your user interface code. Show message boxes and interact with controls in the UI code.
I guessed that TransNo is a numeric type, a primary key and a foreign key. If it is an identity field, you should not be sending a value for it. Remove it from the field list, parameter list and the Add method for the Insert command.
Dates should be saved as dates. You will have problems later sorting and searching if they are not dates. I did not change this is the code.
Private cnStr As String = "Your connection String"
Sub Savepayment(TransNo As Integer, TotalBill As Decimal, str_user As String, Discount As Decimal, Total As Decimal)
Dim sdate As String = Now.ToString("yyyy-MM-dd")
Dim stime As String = Now.ToString("hh:mm:ss")
Using cn As New SqlConnection(cnStr)
Using cm As New SqlCommand("Insert into TblSales(transno,total,sdate,stime,cashier,discount,totalbill) values (#transno,#total,#sdate,#stime,#cashier,#discount,#totalbill);", cn)
With cm.Parameters
.Add("#transno", SqlDbType.NVarChar).Value = TransNo
.Add("#total", SqlDbType.Decimal).Value = TotalBill
.Add("#sdate", SqlDbType.NVarChar).Value = sdate
.Add("#stime", SqlDbType.NVarChar).Value = stime
.Add("#cashier", SqlDbType.NVarChar).Value = str_user
.Add("#Discount", SqlDbType.Decimal).Value = Discount
.Add("#totalbill", SqlDbType.Decimal).Value = Total
End With
cn.Open()
cm.ExecuteNonQuery()
End Using
Using cm As New SqlCommand("Update tblcart set status='Completed' Where transno = #TransNo", cn)
cm.Parameters.Add("#TransNo", SqlDbType.Int).Value = TransNo
cm.ExecuteNonQuery()
End Using
End Using
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim TransNo = CInt(FrmPos.LblTransNo.Text)
Dim User As String = "I don't know where this comes from"
Dim BillTotal As Decimal
If Not Decimal.TryParse(TxtBill.Text, BillTotal) Then
MessageBox.Show("Please enter a valid number in Total Bill.")
Exit Sub
End If
Dim Discount As Decimal
If Not Decimal.TryParse(TxtDiscount.Text, Discount) Then
MessageBox.Show("Please enter a valid number in Discount")
Exit Sub
End If
Dim TotalAfterDiscount As Decimal
If Not Decimal.TryParse(TxtTotal.Text, TotalAfterDiscount) Then
MessageBox.Show("Please enter a valid number in Total")
Exit Sub
End If
Try
Savepayment(TransNo, BillTotal, User, Discount, TotalAfterDiscount)
MsgBox("Payment successfully Saved!", vbInformation)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
'I have no idea what this code is doing.
With FrmReceipt
.PrintPreview("Select c.id, p.Description, c.price, c.qty, c.total From tblcart as c inner join tblproduct as p on c.pid=p.id where c.status like 'Completed' and TransNo like '" & FrmPos.LblTransNo.Text & "'")
.ShowDialog()
End With
With FrmPos
.LblTransNo.Text = .GetTransNo()
.loadcart()
End With
End Sub
I get the solution i add this code of refresh report on the load form event of the report form
ReportViewer1.RefreshReport()
I have looked at many different code snippets on this site looking that would show me how to do something that should be fairly simple once I have the knowledge.
I want to query a database table for an array of values and then populate a combobox with those results.
Here is what I have so far:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Form1.machineName.DisplayMember = "Text"
Dim tb As New DataTable
tb.Columns.Add("Text", GetType(String))
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
For Each cmd As String In cmd
'I want to add each value found in the database to "tb.Rows.Add"
'tb.Rows.Add(???)
Next
Form1.machineName.DataSource = tb
cn.Open()
cmd.ExecuteNonQuery()
End Using
cn.Close()
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I proceeded much like you did. I used the Load method of the DataTable. It is not necessary to set the column name and type. The name of the column is taken from the Select statement and the datatype is inferred by ADO.net from the first few records.
Luckily a DataTable can be an Enumerable using the .AsEnumnerable method. Then we can use Linq to get all the values from the MachineName column. Calling .ToArray causes the Linq to execute. If you hold your cursor over names on this line you will see that the datatype is String(). Just what we need to fill a combo box.
Code for a class called DataAccess
Private ConnectionString As String = "Your Connection String"
Public Function GetMachineNames() As String()
Dim tb As New DataTable
Dim SQL As String = "SELECT MachineName from machine;"
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim names = dt.AsEnumerable().Select(Function(x) x.Field(Of String)("MachineName")).ToArray()
Return names
End Function
In the form load you combo box like this.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim DatAcc As New DataAccess()
Dim arr = DatAcc.GetMachineNames()
machineName.DataSource = arr
End Sub
If you just want the MachineName to be displayed in the ComboBox, then just use that as the DisplayMember; don't bother creating another column called Text.
Public Sub getMachines()
Try
Dim cmd As String = "SELECT MachineName from machine"
Dim ds As New DataSet()
Using con As New MySqlConnection(ConnectionString)
Using da As New MySqlDataAdapter(cmd, con)
da.Fill(ds)
With Form1.machineName
.DisplayMember = "MachineName"
.ValueMember = "MachineName"
.DataSource = ds
End With
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I'll show a few examples, including using parameters, since that is important.
First up, a quick translation to run the existing query and loop through the results:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Form1.machineName.Items.Add(rdr("MachineName"))
End While
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
But better practice for a method like this is to isolate data access for the UI. This method should return results to the caller, which can decide what do with them. So I'll show two methods: one to get the data, and the other to loop through it and set up the combobox:
Private Function GetMachines() As DataTable
'No try/catch needed here. Handle it in the UI level, instead
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
For Each item As DataRow in getMachines().Rows
Form1.machineName.Items.Add(item("MachineName"))
Next
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
Or, we can use DataBinding:
Private Function GetMachines() As DataTable
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
Form1.machineName.DisplayMember = "FirstName";
Form1.machineName.ValueMember = "City"
Form1.machineName.DataSource = GetMachines()
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
If you ever want to use a filter, you might do this (notice the overloading):
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
da.Fill(result)
End Using
Return result
End Function
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Return GetMachines("%")
End Function
Query parameters like that are very important, and if you were doing string concatenation to accomplish this kind of thing on your old platform, you were doing very bad things there, too.
Finally, let's get fancy. A lot of the time, you really don't want to load an entire result set into RAM, as is done with a DataTable. That can be bad. Instead, you'd like be able to stream results into memory and only work with one at a time, minimizing RAM use. In these cases, you get to play with a DataReader... but returning a DataReader object from within a Using block (which is important) doesn't work that well. To get around this, we can use functional programming concepts and advanced language features:
Private Iterator Function GetMachines(ByVal machineFilter As String) As IEnumerable(Of String)
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Dim result As String = rdr("MachineName")
Yield Return result
End While
End Using
End Using
Return result
End Function
Private Function GetMachines() As IEnumerable(Of String)
Return GetMachines("%")
End Function
So, here is a case where I have more than 40.000 rows in my database.
When I start Select * from Table it gets me result set in less than a second.
But, when I want to fill DataTable with those rows and than show it in DataGridView it takes forever to show (Around 15-20 seconds).
Why is that?
My code:
Public Shared Function FillDTwithSQL(ByVal StoredProc As String, ByVal table As DataTable) As DataTable
Dim cmd As SqlCommand = CreateCommand(SProcedura)
Dim dt As New DataTable("dt")
dt = table
dt.Rows.Clear()
Try
If adoConnection.State = ConnectionState.Closed Then adoConnection.Open()
dt.Load(cmd.ExecuteReader(CommandBehavior.Default))
Catch ex As Exception
MsgBox("Greska: " & ex.ToString)
Error = True
Error_text = ex.ToString
End Try
adoConnection.Close()
Return dt
End Function
Public Shared Function CreateCommand(ByVal SProcedura As String) As SqlCommand
CreateConnection(ConnectionSetup.ConnectionString)
Dim cmd As New SqlCommand(ConnectionSetup.DataBaseName & ".dbo." & SProcedura, adoConnection)
cmd.CommandType = CommandType.StoredProcedure
Return cmd
End Function
Private sub FillDGV()
DataBaseLayer.FillDTwithSQL("SelectProc", ds_Tables.Table)
Me.DataGridView1.DataSource = dds_Tables.Table
Me.DataGridView1.ClearSelection()
End Sub
For large amounts of data it is recommended to use the DataGridView's virtual mode.
Have a look at this link: How to: Implement Virtual Mode in the Windows Forms DataGridView Control
To summarize the link: You have to implement your own data caching by implementing the DataGridView's CellValueNeeded event and set Me.DataGridView1.VirtualMode = true.