How to handle DBNull? VB.NET - vb.net

I have form that has datagrid and some textbox controls.
Basically, Im performing Add function. Whenever I add, it scans the maximum value and it will increment by one. I only designed for auto increment. My problem is, how can I increment when the ExecuteScalar gets a null value.
Here's the sample codes
Public Sub IncrementID()
conn = New MySqlConnection
conn.ConnectionString = "server=localhost; userid=root; password=root; database=uecp_cens"
Try
conn.Open()
Dim insert_coupon_query As String = ("SELECT MAX(faculty_ID) from uecp_cens.tblfacultyinfo")
Dim cmd_query As New MySqlCommand(insert_coupon_query, conn)
Dim cmd_result As Integer = CInt(cmd_query.ExecuteScalar()) 'THIS IS WHERE THE ERROR OCCURS, IT GIVES ME 'InvalidCastException was unhandled' Conversion from type 'DBNull' to type 'Integer' is not valid.
txtFacultyNo.Text = cmd_result + 1
Catch ex As MySqlException
MessageBox.Show(ex.Message)
Finally
conn.Dispose()
End Try
End Sub
Any help is appreciated.

Reference For More Help , Click Here
you should write this casting
cmd.CommandText = "SELECT COUNT(*) FROM dbo.region";
Int32 count = (Int32) cmd.ExecuteScalar();

Related

VB.NET How to correctly loop through a result set

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

Select max value in MS Access database

I need to select the max value in my row column. When I hit line
(FindCurrentTimeCard = Val(myreader("Row"))
I get an error:
System.IndexOutOfRangeException
Code:
Public Function FindCurrentTimeCard() As Integer
Dim myconnection As OleDbConnection = New OleDbConnection
Dim query As String = "Select MAX(Row) from Table2"
Dim dbsource As String =("Provider=Microsoft.ACE.OLEDB.12.0;DataSource=S:\Docs\PRODUCTION\Shop Manager\Shop_Manager\Shop_Manager\Database2.accdb;")
Dim conn = New OleDbConnection(dbsource)
Dim cmd As New OleDbCommand(query, conn)
Try
conn.Open()
Dim myreader As OleDbDataReader = cmd.ExecuteReader()
myreader.Read()
FindCurrentTimeCard = Val(myreader("Row"))
conn.Close()
Catch ex As OleDbException
MessageBox.Show("Error Pull Data from Table2")
FindCurrentTimeCard = 1
End Try
End Function
Table2
The issue is that when you evaluate an aggregate function (or indeed, any expression performing some operation on a field or fields), the result of such evaluation will be assigned an alias (such as Expr1000) unless an alias is otherwise stated.
Hence, when you evaluate the SQL statement:
select max(table2.row) from table2
MS Access will return the result assigned to an alias such as Expr1000:
Hence, the SQL statement does not output a column named Row, causing your code to fail when attempting to retrieve the value of such column:
FindCurrentTimeCard = Val(myreader("Row"))
Instead, you should specify an alias to which you may refer in your code, e.g.:
select max(table2.row) as maxofrow from table2
With your function then returning the value associated with such column:
FindCurrentTimeCard = Val(myreader("maxofrow"))
Comments and explanations in-line
Public Function FindCurrentTimeCard() As Integer
Dim CurrentTimeCard As Integer
'The Using block ensures that your database objects are closed and disposed
'even it there is an error
'Pass the connection string directly to the connection constructor
Using myconnection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;DataSource=S:\Docs\PRODUCTION\Shop Manager\Shop_Manager\Shop_Manager\Database2.accdb;")
Dim query As String = "Select MAX(Row) from Table2"
Using cmd As New OleDbCommand(query, myconnection)
Try
myconnection.Open()
'since you are only retrieving a single value
'you can used .ExecuteScalar which gets the value
'in the first row, first column
CurrentTimeCard = CInt(cmd.ExecuteScalar)
Catch ex As OleDbException
MessageBox.Show("Error Pull Data from Table2")
End Try
End Using
End Using
'vb.net uses the Return statement to return the value of the function
Return CurrentTimeCard
End Function

How do I create a dataset in VB.NET from this code?

I currently have the following code in my project which populates a DataGridView object with the results of an SQL query.
Sub PerformQuery(ByVal SQLText As String)
Dim DbConnection As New OleDb.OleDbConnection(createConnectionString)
Dim SQLQuery As String = SQLText
Dim Adapter As New OleDb.OleDbDataAdapter(SQLQuery, DbConnection)
Try
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Elsewhere in my project I can create a DataSet object using the code
Dim ds As New DataSet
And then extract data from it using code like:
MaxRows = ds.Tables("Dataset_Users").Rows.Count
Rather than populating a DataGridView, how can I use the PerformQuery code to create a dataset?
Thank you in advance for your help.
I think you are after the following:
Try
Dim ds As New DataSet
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
ds.Table.Add(Table)
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Or as in your example you was after the Number of rows in the dataset you can do the same with the DataTable, For Example:
Try
Dim MaxRows As Integer
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
'' Getting the number of rows in the DataTable
MaxRows = Table.Rows.Count
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Think in a more functional style. Return the table instead of setting to grid. While we're here, let's update the method so you don't have to write queries anymore that leave you wide open to sql injection attacks:
Function PerformQuery(ByVal SQLText As String, ByVal ParamArray Parameters() As OleDbParameter) As DataTable
Dim result As New DataTable()
Using cn As New OleDb.OleDbConnection(createConnectionString), _
cmd As New OleDb.OleDbCommand(SQLText, cn), _
Adapter As New OleDb.OleDbDataAdapter(cmd, cn)
If Parameters IsNot Nothing AndAlso Parameters.Length > 0 Then
cmd.Parameters.AddRange(Parameters)
End If
Adapter.Fill(result)
End Using
Return Result
End Function
Note that I also removed the error handling and locale code. You still need to do that stuff, but when you want to just return a datatable instead of interact directly with the user interface in a method you have effectively moved your code to a lower level of abstraction. When you do that, you probably don't want to deal with the error handling at this lower level any more; instead let the exceptions bubble up where you can handle them closer to the user interface.
Now you call the updated method like this:
Dim sql As String = "SELECT * FROM Customers WHERE CustomerID = ?"
Dim CustID As New OleDb.OleDbParameter("CustomerId", OleDbType.Integer)
CustID.Value = 123456
Try
DataGridView1.DataSource = PerformQuery(sql, CustID)
Catch Ex As Excpetion
MsgBox(Ex.Message)
End Try

How to solve this: Conversion from type DBNull to type string is not valid

I think this is were I think the error is showing when I close the crystal report after creating a report.
Private Sub Populate(id As Integer, perdate As Date, controlnum As String, establishmentname As String, fname As String, mname As String, lname As String, address As String, pertype As String, ornum As String, amntpd As String, datepd As Date)
Dim row As String() = New String() {id, perdate, controlnum, establishmentname, fname, mname, lname, address, pertype, ornum, amntpd, datepd}
DataGridView1.Rows.Add(row)
End Sub
This is where the code for retrieving data from my database(MySql database) to datagrid view
Private Sub retrieve()
DataGridView1.Rows.Clear()
sql = "SELECT * FROM tblfsesmis"
Try
Using con
con.Open()
cmd = New MySqlCommand(sql, con)
adapter = New MySqlDataAdapter(cmd)
adapter.Fill(dt)
For Each row In dt.Rows
'It's in your Populate function that you want to check the DBNull values
Populate(row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7), row(8), row(9), row(10), row(11))
Next
dt.Rows.Clear()
DataGridView1.Refresh()
End Using 'con Object will be disposed automatically
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Can you help me in how to solve this? please ! thank you
If your application doesn't support null values for a field then modify your query so that null values cant appear.
So instead of: SELECT * FROM tblfsesmis
Use SELECT Id, PerDate, IsNull(ControlNum, '') .... FROM tblfsesmis.
IsNull will replace null values with a default that you have chosen.
Alternatively you can fix this in code with something like
Dim result = If(Convert.IsDBNull(val), "", val) on each field which could be null.
However as pointed out in the comment you don't appear to need these values to be cast to strings anyway. So just assign the datatable directly to the datasource.
Private Sub retrieve()
sql = "SELECT * FROM tblfsesmis"
Try
Using con
con.Open()
cmd = New MySqlCommand(sql, con)
adapter = New MySqlDataAdapter(cmd)
adapter.Fill(dt)
DataGridView1.DataSource = dt
End Using 'con Object will be disposed automatically
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Use DataRow.Field(Of T) extension method.
With DataRow.Field(Of String)(columnIndex) if value is DbNull then Nothing/null will be returned.
Populate(row.Field(Of Integer)(0), row.Field(Of String)(1))
If column of type Integer can be a NULL too, then use Nullable with GetValueOrDefault method
Populate(row.Field(Of Integer?)(0).GetValueOrDefault(), row.Field(Of String)(1))
But as #F0r3v3r-A-N00b suggested - you can use DataTable as datasource for DataGridView.
Private Sub retrieve()
Dim sql As String = "SELECT * FROM tblfsesmis"
Try
Using con As New MySqlConnection(yourConnectionString)
con.Open()
Using cmd As New MySqlCommand(sql, con)
Dim adapter As New MySqlDataAdapter(cmd)
Dim dt As New DataTable()
adapter.Fill(dt)
DataGridView1.DataSource = dt
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
In this way DataGridView will be updated automatically. Just check that DataGridView.AutoGenerateColumns = true(it is true by default)

Giving the value of a query to a variable

Help, I am using SQL Server as my database, and my back-end is VB.NET.
I want to assign the value of this query:
SELECT sum(productPrice) from cartTbl
to a variable, and then give the value to a textbox called totalPrice.
How do I perform this? Thank you in advance!
you can use
SELECT #var1=sum(productPrice) from cartTbl
Use an alias for your calculated column
SELECT sum(productPrice) as prod_sum
from cartTbl
Then you can read it like this
While dr.Read()
totalPrice.Text = dr("prod_sum")
End While
It is as simple as this, but please read some basic info on ADO.NET
Using con = new SqlConnection(.....constring here ....)
Using cmd = new SqlCommand("SELECT sum(productPrice) from cartTbl", con)
con.Open()
Dim result = cmd.ExecuteScalar()
Console.WriteLine(result)
End Using
End Using
You should use ExecuteScalar() if using ADO.NET
Public Function GetProductPrice() As Integer
Dim ProdPrice As Int32 = 0
Dim sql As String = "SELECT sum(productPrice) from cartTbl"
Using conn As New SqlConnection(connString)
Dim cmd As New SqlCommand(sql, conn)
Try
conn.Open()
ProdPrice = Convert.ToInt32(cmd.ExecuteScalar())
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
Return ProdPrice
End Function
You can then call this method to get the Price.
Dim prodPrice = GetProductPrice()
To expand on what has already been said, you could use the following to make it a little more flexable:
Private Sub Test()
'Get/set connection string
Me.TextBox1.Text = Me.SQLExecuteScalar(ConnectionString, "SELECT sum(productPrice) FROM cartTbl")
End Sub
Public Shared Function SQLExecuteScalar(ByVal ConnectionString As String, ByVal Query As String) As String
Dim Result As String = Nothing
Dim Exc As Exception = Nothing
Using Conn As New SqlClient.SqlConnection(ConnectionString)
Try
'Open the connection
Conn.Open()
'Create the SQLCommand
Using Cmd As New SqlClient.SqlCommand(Query, Conn)
'Create an Object to receive the result
Dim Obj As Object = Cmd.ExecuteScalar
If (Obj IsNot Nothing) AndAlso (Obj IsNot DBNull.Value) Then
'If Obj is not NULL
Result = Obj.ToString
End If
End Using
Catch ex As Exception
'Save error so we can (if needed) close the connection
Exc = ex
Finally
'Check if connection is closed
If Not Conn.State = ConnectionState.Closed Then
Conn.Close()
End If
End Try
End Using
'Check if any errors where found
If Exc IsNot Nothing Then
Throw Exc
End If
Return Result
End Function