Sorting a Database structure in visual basic - vb.net

I have this structure:
Module Global_Variables
Public myrecords(10) As myDatabase
Public counter As Integer = 0
Public Structure myDatabase
'creates database Structure For input data
<VBFixedString(30)> _
Dim Driver As String '30 bytes
Dim Car As Integer '4 bytes
<VBFixedString(15)> _
Dim Team As String '15 bytes
Dim Grid As Integer '4 bytes
Dim FastestLap As Double '8 bytes
Dim RaceTime As Double '4 bytes
Dim Points As Double '4 bytes
End Structure
End Module
The program receives data from the user and then displays the data in a text box called txtOutput in another form:
myrecords(counter).Driver = driver_box.Text
myrecords(counter).Car = car_box.Text
myrecords(counter).Team = team_box.Text
myrecords(counter).Grid = grid_box.Text
myrecords(counter).FastestLap = fl_box.Text
myrecords(counter).RaceTime = rt_box.Text
myrecords(counter).Points = points_box.Text
Form_Display.txtDisplay.AppendText(myrecords(counter).Driver & " " &
myrecords(counter).Car & " " & myrecords(counter).Team & " " &
myrecords(counter).Grid & " " & myrecords(counter).FastestLap & " " &
myrecords(counter).RaceTime & " " & myrecords(counter).Points & vbCrLf)
counter = counter + 1
MsgBox("Submit success!")
Call input_box_clear()
The user can then click a button to sort the records in ascending order by fastest lap. How do I do this?
I have tried algorithms such as bubble sort and selection sort but neither worked.
Thank you

Declaring type (class, not structure)
Public class RaceData
Public Property Driver As String
Public Property Car As Integer
Public Property Team As String
Public Property Grid As Integer
Public Property FastestLap As Double
Public Property RaceTime As Double
Public Property Points As Double
End Class
In-memory database (look what is in System.Collections)
Private _raceDb As New List(Of RaceData)()
Add user input
Dim newItem As New RaceData()
newItem.Driver = driver_box.Text
newItem.Car = Integer.Parse(car_box.Text)
newItem.Team = team_box.Text
newItem.Grid = Integer.Parse(grid_box.Text)
newItem.FastestLap = Double.Parse(fl_box.Text)
newItem.RaceTime = Double.Parse(rt_box.Text)
newItem.Points = Double.Parse(points_box.Text)
_raceDb.Add(newItem)
Sorting for the grid (Read about LINQ)
// sort by fastest race time
Dim sortedDb As List(Of RaceData) = _raceDb.OrderBy(Function(x) x.RaceTime).ToList()
Pick one fastest race
Dim fastest As RaceData = _raceDb.OrderBy(Function(x) x.RaceTime).FirstOrDefault()
If fastest IsNot Nothing Then ...
Build a string for each item to add to multi-line textbox
Dim lines() As String = _raceDb.Select(Function(x) x.Driver & " --- " & x.Team).ToArray()
' Using some tips from the comments
Dim lines() As String = _raceDb.
Select(Function(x) string.Format("{0,-30} --- {1,15}", x.Driver, x.Team)).ToArray()

Related

Error on Loop Through Outlook MailItems using Late Binding in Access VBA: User-defined Type Not Defined

I'm trying to filter Outlook's Deleted Items folder then loop through all the items that fit the criteria.
I'm using late binding. There's a problem with the way I'm declaring.
Here are my variables
Public OutlApp As Object
Public OutlNameSpace As Object
Public OutlMail As Object
Public OutlAttach As Object
Public OutlFolder As Object
Public OutlItem As Object
Public OutlMailItem As Object
Public OutlSenderLogin As String
Public OutlSenderName As String
Public OutlSenderEMail As String
Public OutlDateReceived As String
Public OutlDateSent As String
Public OutlSubject As String
Public OutlMsgBody As String
Public OutlSubjectCriteria1 As String
Public OutlSubjectCriteria2 As String
Public OutlSubjectCriteria3 As String
Public OutlSubjectCriteria4 As String
Public OutlFilter As String
Public OutlStartDate As String
Public OutlEndDate As String
Public OutlSentBy As String
Public OutlSentBy2 As String
Public OutlSentBy3 As String
I get an error at "If TypeOf OutlFolder.Items(i) Is MailItem Then", the bolded part is highlighted.
The error is "User-defined Type Not Defined".
Option Compare Database
Option Explicit
Const OutlFolderInbox As Integer = 6
Const OutlFolderIDeletedItems As Integer = 3
Public Function OutlookDeletedItems()
CurPath = CurrentProject.Path & "\"
Dim i, CountOfItems As Long
Dim EmailContTD, EmailContNew As String
Set OutlApp = GetObject(, "Outlook.application")
Set OutlNameSpace = OutlApp.GetNamespace("MAPI")
Set OutlFolder = OutlNameSpace.GetDefaultFolder(OutlFolderIDeletedItems)
Set OutlMail = GetObject(, "Outlook.MailItem")
OutlMyUTC = 7
OutlStartDate = Format(DateAdd("h", -OutlMyUTC, Date), "\'m/d/yyyy\") & " 12:00 AM'"
OutlSentBy = "hhh, fff" '
OutlSentBy2 = "fffff#service-now.com" '/
OutlSubjectCriteria1 = "blah *"
OutlSubjectCriteria2 = "blah"
OutlSubjectCriteria3 = "blah"
OutlFilter = "#SQL= ((urn:schemas:httpmail:sendername = '" & OutlSentBy & "' OR urn:schemas:httpmail:sendername = '" & OutlSentBy2 & "') And urn:schemas:httpmail:datereceived >= " & OutlStartDate & _
") AND (urn:schemas:httpmail:subject = '" & OutlSubjectCriteria3 & "' OR urn:schemas:httpmail:subject = '" & OutlSubjectCriteria2 & "' OR urn:schemas:httpmail:subject Like '" & OutlSubjectCriteria1 & "') "
CountOfItems = OutlFolder.Items.Restrict(OutlFilter).Count
If CountOfItems = 0 Then
Exit Function
End If
Set OutlMailItem = OutlFolder.Items.Restrict(OutlFilter)
With OutlItem
For i = CountOfItems To 1 Step -1
If TypeOf OutlFolder.Items(i) Is MailItem Then
Set OutlMailItem = OutlFolder.Items(i)
OutlDateReceived = OutlMailItem.ReceivedTime
OutlSubject = OutlMailItem.Subject
OutlMsgBody = OutlMailItem.Body
If OutlSubject Like OutlSubjectCriteria1 Then
EmailContTD = Replace(OutlMsgBody, Chr(34), "")
End If
If OutlSubject = OutlSubjectCriteria2 Then
EmailContNew = Replace(OutlMsgBody, Chr(34), "")
DoCmd.RunSQL "INSERT INTO SNNew ( Contents ) SELECT """ & EmailContNew & """ AS Expr1 FROM DUAL;"
End If
If OutlSubject = OutlSubjectCriteria3 Then
For Each OutlAttach In OutlItem.Attachments
OutlAttach.SaveAsFile CurPath & "_Load\MyTickets.xlsx"
Next OutlAttach
End If
End If
EmailContTD = ""
EmailContNew = ""
Next
End With
End Function
If TypeOf OutlFolder.Items(i) Is MailItem Then
The MailItem class isn't defined, the compiler isn't lying - you would need to reference the Outlook library (or otherwise define a MailItem class) for that code to compile.
You can use the TypeName function for late-bound type checks (note, it's less robust than the compile-time check):
If TypeName(OutlFolder.Items(i)) = "MailItem" Then
Make sure Option Explicit is at the top of every module, too: late binding (explicit or not) already makes a lot of typos blow things up at run-time (error 438; prefer early binding whenever possible, the compiler is then able to pick up problems much earlier). With this option on typos won't become on-the-fly Variant values that can produce weird unexpected bugs.

Create a vb class using a text file. The values for class name and attributes should come from database

I want to create an executable that will use a text file as a template and create a class in vb.net
The class name and its attributes are stored in the database. The user should be able to input the class name. The ClassName and AttributeName tags in the template are to be replaced by the corresponding values from the database.
Any lead would be appreciated. Thank You.
Code files are just text files. Generating code files is a bit confusing because you have source code appearing as string literals, but in a technical sense it isn't very difficult:
Sub GenerateCode()
Dim dtb As New DataTable
dtb.Columns.Add("ClassName")
dtb.Columns.Add("PropertyName")
dtb.Columns.Add("PropertyType")
'NOTE: datatable must be sorted by ClassName
dtb.Rows.Add("ClassOne", "PropertyOne", "String")
dtb.Rows.Add("ClassOne", "PropertyTwo", "Integer")
dtb.Rows.Add("ClassOne", "PropertyThree", "String")
dtb.Rows.Add("ClassTwo", "AnotherPropertyOne", "String")
dtb.Rows.Add("ClassTwo", "AnotherPropertyTwo", "Integer")
dtb.Rows.Add("ClassTwo", "AnotherPropertyThree", "String")
Dim strFilename As String = ""
Dim strParentFolder As String = "C:\Junk"
If Not System.IO.Directory.Exists(strParentFolder) Then
System.IO.Directory.CreateDirectory(strParentFolder)
End If
Dim strPreviousClassName As String = ""
Dim sb As New System.Text.StringBuilder
For Each drwProperty As DataRow In dtb.Rows
Dim strThisClassName As String = drwProperty("ClassName").ToString
If strThisClassName <> strPreviousClassName Then
'class name has changed
If strPreviousClassName > "" Then
'write previous class
sb.AppendLine("End Class")
strFilename = strParentFolder & "\" & strPreviousClassName & ".vb"
My.Computer.FileSystem.WriteAllText(strFilename, sb.ToString, False)
End If
'start new class
sb = New System.Text.StringBuilder
sb.AppendLine("Class " & strThisClassName)
strPreviousClassName = strThisClassName
End If
'append property
sb.AppendLine(" Property " & drwProperty("PropertyName").ToString & " As " & drwProperty("PropertyType").ToString)
Next drwProperty
'Write last class
sb.AppendLine("End Class")
strFilename = strParentFolder & "\" & strPreviousClassName & ".vb"
My.Computer.FileSystem.WriteAllText(strFilename, sb.ToString, False)
End Sub

How can we make this code run with option strict on?

Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblSystemSerialNumbers.Text = SystemSerialNumber()
lblCpuIds.Text = CpuId()
End Sub
Private Function SystemSerialNumber() As String
' Get the Windows Management Instrumentation object.
Dim wmi As Object = GetObject("WinMgmts:")
' Get the "base boards" (mother boards).
Dim serial_numbers As String = ""
Dim mother_boards As Object = wmi.InstancesOf("Win32_BaseBoard")
For Each board As Object In mother_boards
serial_numbers &= ", " & board.SerialNumber
Next board
If serial_numbers.Length > 0 Then serial_numbers = serial_numbers.Substring(2)
Return serial_numbers
End Function
Private Function CpuId() As String
Dim computer As String = "."
Dim wmi As Object = GetObject("winmgmts:" & _
"{impersonationLevel=impersonate}!\\" & _
computer & "\root\cimv2")
Dim processors As Object = wmi.ExecQuery("Select * from Win32_Processor")
Dim cpu_ids As String = ""
For Each cpu As Object In processors
cpu_ids = cpu_ids & ", " & cpu.ProcessorId
Next cpu
If cpu_ids.Length > 0 Then cpu_ids = cpu_ids.Substring(2)
Return cpu_ids
End Function
End Class
This code will retrieve the CPU id and the motherboard id. How do I ensure that this will work even when option strict is on.
Why this could be a problem?
Well, let's see. The type of wmi is Object. That wmi do not necessarily support methods like InstancesOf, and SerialNumber
So how can we pull this out?
I think object that we got from GetObject is not just pure object. I think we should ctype or direct cast it to a more appropriate type. That more appropriate type will support methods like InstancesOf, SerialNumber, etc.
However what are the appropriate types?
You could use the ManagementObjectSearcher from the WMI classes hosted inside the System.Management.dll assembly.
(And you need to add the appropriate reference).
In this way you could write the SystemSerialNumber as
Private Function SystemSerialNumber(computer As String) As String
Dim wmi = New ManagementObjectSearcher(computer & "\root\cimv2", "select * from Win32_BaseBoard")
Dim boards = New List(Of String)()
For Each board In wmi.Get()
Dim temp = board.Properties("SerialNumber").Value?.ToString()
If Not String.IsNullOrEmpty(temp) Then
boards.Add(temp)
End If
Next board
Return String.Join(", ", boards)
End Function
The CpuId function is a bit more complex because you want to set the Impersonate flags but it is still possible to write a method around the NET wrapper classes for WMI interfaces
Private Function CpuId(computer As String) As String
Dim cpu = New List(Of String)()
Dim options = New ConnectionOptions()
options.Impersonation = System.Management.ImpersonationLevel.Impersonate
Dim scope = New ManagementScope(computer & "\root\cimv2", options)
scope.Connect()
Dim query = New ObjectQuery("Select * from Win32_Processor")
Dim wmi = New ManagementObjectSearcher(scope, query)
For Each m As ManagementObject In wmi.Get()
Dim temp = m.Properties("ProcessorId").Value?.ToString()
If Not String.IsNullOrEmpty(temp) Then
cpu.Add(temp)
End If
Next
Return String.Join(", ", cpu)
End Function

Visual Basic - system.nullReferenceException

So I'm still a bit of a newbie when it comes to programming, hence why I'm using visual basic. I'm getting this exception raised repeatedly, but the variables that vb is saying have unassigned values have been given values in my code. Can anyone point out where I'm going wrong with this?
EDIT: just a few more details: the file exists, I can read from it using just the ReadLine method, but I need to split the fields so I can compare the scores and get the highest 2 scores
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim srdFile As System.IO.StreamReader
Dim strLine As String
Dim strField(1) As String
Dim strName() As String
Dim strScore() As String
Dim i = 0
srdFile = New System.IO.StreamReader("HighScores.dat")
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(",")
strName(i) = strField(0)
strScore(i) = strField(1)
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i = i + 1
Loop
End Sub
Following two arrays are never initialized: strName and strScore
I don't know the logic, but one way would be to use a List(Of String) instead which does not need to get the correct size in the first place and can be resized. I would also use the Using-statement to dispose the stream properly:
Using srdFile As New System.IO.StreamReader("HighScores.dat")
Dim strLine As String
Dim strField(1) As String
Dim strName As New List(Of String)
Dim strScore As New List(Of String)
Dim i = 0
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(","c)
strName.Add(strField(0))
strScore.Add(strField(1))
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i += 1
Loop
End Using
Side-note: i recommend to set Option Strict to On by default.
By the way, here is a completely different approach doing the same but with LINQ:
Dim lines = From line In IO.File.ReadLines("HighScores.dat")
Where Not String.IsNullOrWhiteSpace(line)
Let fields = line.Split(","c)
Let name = fields.First()
Let score = fields.Last()
Select String.Format("{0}, {1}", name, score)
rtbOut.Text = String.Join(Environment.NewLine, lines)
I find this more readable.
Before you use an array, you need to assign a fixed array size in the computer memory locations. You can do this by initialising an array with the number of array elements. In your code, you have not allocated any memory to strName() and strScore() before using them, hence the code will throw an exception.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim srdFile As System.IO.StreamReader
Dim strLine As String
Dim strField(1) As String
Dim strName(10) As String ''fixed size array (Using List(Of T) is a better option)
Dim strScore(10) As String ''fixed size array (Using List(Of T) is a better option)
Dim i = 0
srdFile = New System.IO.StreamReader("HighScores.dat")
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(",")
strName(i) = strField(0)
strScore(i) = strField(1)
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i = i + 1
Loop
End Sub
You can also create a dynamic array. Please follow Resizing an array at runtime in VB.NET on Stackoverflow about dynamic array.

asp.net vb website loop through database rows

I am working on my first website and need help with a loop. I have a database table containing food items named Menu with 8 categories (such as Burgers, Appetizers). I also have a menu page on website with 8 different pics to display items from each category. I need to loop through rows of database. What is happening is it's only looping through columns and repeating first line over and over. I'm aware I need a loop but for some reason cannot get that right.
This is code behind:
Partial Class Burger
Inherits System.Web.UI.Page
'String Used to build the necessary markup and product information
Dim str As String = ""
'Var used to interact with SQL database
Dim db As New Interaction
'Adds the necessary markup for each menu item, using its productName
Protected Sub printMenuBlock(ByVal productName As String)
'Set up variable storing the product
Dim product As Product
'Pull the product in from our database using the productName
product = db.ReadProduct(productName)
'Add necessary markup to str variable, with products information within
str += "<div class='storeItem'>"
' str += " <img alt='Item Picture' class='itemPicture' src='" + product.ImagePath.Substring(3).Replace("\", "/") + "' />"
' str += " <div class='itemInfo'>"
str += " <h1 class='itemName'>"
str += " " + product.Name + "</h1>"
str += " <h3 class='itemDescription'>"
str += " " + product.Description + "</h3>"
str += " <p class='itemPrice'>"
str += " " + product.Price.ToString("c") + "</p>"
str += " "
str += " </div>"
str += " </div>"
End Sub
'Uses
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim productNames As New List(Of String)
'Pull the product names using the database
productNames = db.getProductNames
'Loop through all product names
For Each name As String In productNames
'Add necessary markup and product info to str variable
printMenuBlock(name)
Next
'Print the str variable in our menuPlace div
menuPlace.InnerHtml = str
End Sub
End Class
This is functions from interaction class:
Private Sub GetProduct(ByVal CatIn As String)
' SQL String
Dim strSelect As String
strSelect = "SELECT * "
strSelect &= " FROM Menu "
' strSelect &= " WHERE (ProductCat = 'Burgers')"
' Set up the connection to the datebase
cmdSelect.Connection = conIn.Connect
' Add the SQL string to the connection
cmdSelect.CommandText = strSelect
' Add the parameters to the connection
cmdSelect.Parameters.Add("#CatIn", SqlDbType.NVarChar).Value = CatIn
End Sub
'Executes the SQL statement to find a Product by ProductId
Public Function ReadProduct(ByVal CatIn As String) As Product
' Product object initalized to nothing
Dim prod As Product = Nothing
Try
Call GetProduct(CatIn)
Dim dbr As SqlDataReader
Dim strCat As String
Dim strName As String
Dim strDesc As String
Dim decPrice As Decimal
Dim strPath As String
' Execute the created SQL command from GetProduct and set to the SqlDataReader object
dbr = cmdSelect.ExecuteReader
dbr.Read()
' Check if there are any returned values
If dbr.HasRows Then
' Assign the value in column two to strName
strCat = dbr.GetString(1)
' Assign the value in column two to strName
strName = dbr.GetString(2)
' Assign the value in column three to strDesc
strDesc = dbr.GetString(3)
' Assing the value in column four to intPrice
decPrice = ToDecimal(dbr.GetValue(4))
'Assign the value in column five to strPath
'strPath = dbr.GetString(3)
' Create the new Product object from the returned values
prod = New Product(strName, strDesc, decPrice, strCat, strPath)
End If
' Clear the SQL parameters and close the connection
cmdSelect.Parameters.Clear()
dbr.Close()
Catch ex As SqlException
Dim strOut As String
strOut = ex.Message
Console.WriteLine(strOut)
End Try
' Return the Product object
Return prod
End Function
'Returns a list of Product Names
Public Function getProductNames() As List(Of String)
Dim list As New List(Of String)
Dim sql As String = "SELECT ProductName FROM Menu " +
"WHERE (ProductCat) = 'Burgers'"
'"DISTINCT 'ProductName'"
cmdSelect.CommandText = sql
cmdSelect.Connection = conIn.Connect
Dim dbr As SqlDataReader
dbr = cmdSelect.ExecuteReader
If dbr.HasRows Then
Do While dbr.Read()
list.Add(dbr.GetString(0))
Loop
End If
dbr.Close()
Return list
End Function
There is obviously a Product Class but don't think that is necessary to show on here.
Also, ignore the string path, that will be for images later. Thanks for any help. I'm pretty sure instead of do while I need a for each somewhere but just can't get her done. Thanks in advance.
Products Class:
Public Class Product
Private pName As String
Private pDescription As String
Private pPrice As Integer
Private pPath As String
Private pCat As String
'Constructor, uses database to populate properties based on productName
Public Sub New(ByVal productName As String)
Dim data As New Interaction
Dim work As Product
work = data.ReadProduct(productName)
pCat = work.Cat
pName = work.Name
pDescription = work.Description
pPrice = work.Price
End Sub
'Constructor, populates properties from passed in values
Public Sub New(ByVal NameIn As String,
ByVal DescriptionIn As String, ByVal PriceIn As Integer, ByVal CatIn As String, ByVal ImagePathIn As String)
pName = NameIn
pDescription = DescriptionIn
pPrice = PriceIn
pPath = ImagePathIn
pCat = CatIn
End Sub
'Stores name of product
Public ReadOnly Property Name() As String
Get
Return pName
End Get
End Property
'Stores a description of the product
Public ReadOnly Property Description() As String
Get
Return pDescription
End Get
End Property
'Stores the price of the product
Public ReadOnly Property Price() As Integer
Get
Return pPrice
End Get
End Property
'Stores the path to the image associated with this product
Public ReadOnly Property ImagePath() As String
Get
Return pPath
End Get
End Property
'Stores name of product
Public ReadOnly Property Cat() As String
Get
Return pCat
End Get
End Property
End Class
Use this instead
Public Function ReadProduct(ByVal CatIn As String) As List(Of Dictionary(String, Of String))
Dim ReturnProducts As New List(Of Dictionary(String, Of String))
Try
Call GetProduct(CatIn)
Dim dbr As SqlDataReader
' Execute the created SQL command from GetProduct and set to the SqlDataReader object
dbr = cmdSelect.ExecuteReader
Dim FieldCount = dbr.FieldCount()
Dim ColumnList as New List(Of String)
For i as Integer = 0 to FieldCount - 1
ColumnList.Add(dbr.GetName(i))
Next
While dbr.Read()
Dim ReturnProduct As New Dictionary(String, Of String)
For i as Integer = 0 to FieldCount - 1
ReturnProduct.Add(ColumnList(i), dbr.GetValue(i).toString())
Next
ReturnProducts.Add(ReturnProduct)
End While
cmdSelect.Parameters.Clear()
dbr.Close()
Catch ex As SqlException
Dim strOut As String
strOut = ex.Message
Console.WriteLine(strOut)
End Try
' Return the Product object
Return ReturnProducts
End Function
then, inside printMenuBlock, you declare product with
Dim product = db.ReadProduct(productName)
and later, you access it like so
For i as Integer = 0 to product.Count - 1
'do everything normally for building str except, for example, if you want
'to acccess product.Name as before, access it with product(i).Item("Name"),
'assuming that your column name/alias for "Name" is in fact "Name"
'i personally like to align column names to variable names for laziness's sake
'bad obfuscation practice tho if you don't use aliases
Next