vb.net Formatting for high performance - vb.net

I have recently noticed on my performance assist tool I use on Visual Studio 2013 any time I make a complex string
as a sample string:
"SELECT * from calls where randid = '" & randid & "'"
it will instead recommend using string format
String.Format("SELECT * from calls where randid = '{0}'", randid)
same with using value initializers
instead of
Dim cb As New MySqlConnectionStringBuilder
cb.Database = dbfile
cb.Server = dbserver
cb.UserID = dbuser
cb.Password = dbpassw
use
Dim cb As New MySqlConnectionStringBuilder() With {.Database = dbfile, .Server = dbserver, .UserID = dbuser, .Password = dbpassw}
Do these formats actually improve the performance of an application or is it recommending these for aesthetics?

Out of curiosity (I am absolutely sure that mini-optimizations are devilish) I have tested the difference with this code using LinqPad:
Sub Main
Dim test = "999"
Dim sw = new Stopwatch()
sw.Start()
for i = 0 to 100000
Dim s = "SELECT * FROM TABLE WHERE FIELD = '" + test + "'"
Next
sw.Stop()
sw.Elapsed.Dump("Concat")
sw = new Stopwatch()
sw.Start()
for i = 0 to 100000
Dim s = string.Format("SELECT * FROM TABLE WHERE FIELD = '{0}'", test)
Next
sw.Stop()
sw.Elapsed.Dump("Format")
End Sub
with these results:
Concat
00:00:00.0101903
Format
00:00:00.0365234
The output changes noticeably if we use an integer for the test variable because now the concatenation should use ToString()
Dim test = 999
Concat
00:00:00.0198107
Format
00:00:00.0485193
So, for whatever reason your tool suggest to use the string.Format approach it is not to get better performance.

In the first one compiler will generate String.Concat method call for your string concatenation:
return "test" + value + "test";
it translated into
IL_0000: ldstr "test"
IL_0005: ldarg.0
IL_0006: box [mscorlib]System.Int32
IL_000b: ldstr "test"
IL_0010: call string [mscorlib]System.String::Concat(object, object, object)
IL_0015: ret
Both String.Concat and String.Format will cause boxing (I assumed randid is an int). You should call ToString() on it anyway to make it better.
The second one is a lay. Compiler will generate properties assignments anyway, so
Dim cb As New MySqlConnectionStringBuilder() With {.Database = dbfile, .Server = dbserver, .UserID = dbuser, .Password = dbpassw}
is transformed into
Dim cb As New MySqlConnectionStringBuilder
cb.Database = dbfile
cb.Server = dbserver
cb.UserID = dbuser
cb.Password = dbpassw
by compiler.

Related

How to increment number with current year + character string?

Public Sub incrPR()
Dim curValue As Integer
Dim result As String
Dim yr As String = Now.Year.ToString()
Dim txt As String = "PR"
Using con As SqlConnection = New SqlConnection(ConString)
con.Open()
Dim cmd = New SqlCommand("Select MAX(SpecialOrderNo) FROM SpecialOrder", con)
result = cmd.ExecuteScalar().ToString
If String.IsNullOrEmpty(result) Then
result = "000"
End If
Int32.TryParse(result, curValue)
curValue += 1
result = curValue.ToString("D3") + "-" + yr + "-" + txt
txtno.Text = result
End Using
End Sub
Expected output:
001-2018-PR
002-2018-PR
003-2018-PR
The specific reason that you're not seeing the behaviour you want is here:
Int32.TryParse(result, curValue)
If result is "001-2018-PR" then TryParse will fail, i.e. return False, and that means that curValue will be zero. That means that you are going to get zero EVERY time. If you want to parse the first part of the text then do that rather than parsing the whole thing, e.g.
Int32.TryParse(result.Split("-"c)(0), curValue)
I would change things significantly but that will fix your immediate issue.

Web service returns [] rather than the actual List

I am going through legacy code and not that familiar with VB or Webservices tbh. I need this to display as an array but when running the operation, it will not return the actual list items. I get the following instead:
anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" xmlns:d1p1="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/" d1p1:type="q1:string">[]</anyType
What am I missing?
Public Shared Function GetVehicleFromVINforDecode(ByVal vin As String)
Dim VINlist As New List(Of VINResult)
Dim strPattern As String
strPattern = GetVinPattern(vin)
Using conn As New SqlConnection(ConnectionStrings.CKConnectionString)
Dim sqlComm As New SqlCommand("select * from VehicleData.dbo.vin_pattern where vin_pattern = '" & strPattern & "'", conn)
conn.Open()
Using r As SqlDataReader = sqlComm.ExecuteReader
While r.Read()
Dim VINitem = New VINResult()
VINitem.Make = (r("Make"))
VINitem.Model = (r("Model"))
VINitem.Year = (r("Year"))
VINitem.Cylinders = (r("DEF_ENGINE_CYLINDERS"))
VINitem.Drive = (r("DRIVE_TYPE"))
VINitem.Liters = (r("DEF_ENGINE_SIZE"))
VINitem.Style = (r("Style"))
VINitem.TransmissionSpeed = (r("DEF_TRANS_SPEEDS"))
VINitem.TransmissionType = (r("DEF_TRANS_TYPE"))
VINitem.Trim = (r("Trim"))
VINitem.FuelType = (r("FUEL_TYPE"))
VINlist.AddRange(VINitem)
End While
End Using
End Using
Return VINlist
End Function
A List(Of T) and an array are not the same things, and from my experience here on Stack Overflow web services usually don't have very good support for the former.
Try calling ToArray() on the list when returning it in order to convert it into a regular array:
Return VINlist.ToArray()

Set values to array and then do for each

How would I set following into array:
Public Function opcijeMp(ByVal hwid As String)
Dim hardware As String = hwid
Dim result = New List(Of String)()
Try
ManageConnection(False, konekcija) 'Open connection'
Dim strQuery As String = "SELECT * FROM info.opcije_mp as mp inner join instalacije as i where mp.idopcije_mp =
i.opcijeMP and i.instalacije_hwid = '" + Globals.cpuid + "';"
Dim SqlCmd As New MySqlCommand(strQuery, dbCon)
Dim reader As MySqlDataReader = SqlCmd.ExecuteReader()
While reader.Read()
Globals.prodaja = reader.GetString("Prodaja")
Globals.Kalkulacije = reader.GetString("Kalkulacije")
Globals.Zaduznice = reader.GetString("Zaduznice")
Globals.Predisponacije = reader.GetString("Predisponacije")
Globals.Robno = reader.GetString("Robno")
Globals.KUF = reader.GetString("KUF")
Globals.KIF = reader.GetString("KIF")
Globals.Narudzbenice = reader.GetString("Narudzbenice")
Globals.Nalozi = reader.GetString("Nalozi")
Globals.akcijskeCijene = reader.GetString("Akcijske_cijene")
Globals.servisnaRoba = reader.GetString("Servisna_roba")
Globals.Ostalo1 = reader.GetString("Ostalo1")
Globals.Ostalo2 = reader.GetString("Ostalo2")
Globals.Ostalo3 = reader.GetString("Ostalo3")
End While
reader.Close()
'Vraća podatke u Listi stringova
'Return result
Catch ex As MySqlException
Console.WriteLine("Error: " & ex.ToString())
Return Nothing
Finally
ManageConnection(True, konekcija)
End Try
End Function
So i can use it in next function with for each loop:
Dim s As String = Globals.prodaja
Dim parts As String() = s.Split(New Char() {","c})
Dim icona As String = parts(1)
Dim barmanager1 As New BarManager
Dim TileBarItem = New TileBarItem()
TileBarItem.Content = parts(3)
TileBarItem.Name = "ffss"
TileBarItem.Width = 150
Icon = New BitmapImage(New Uri("pack://application:,,,/DevExpress.Images.v16.1;component/Images/" + icona + ""))
TileBarItem.TileGlyph = Icon
TileBarItem.Background = New SolidColorBrush(DirectCast(ColorConverter.ConvertFromString(parts(2)), Color))
MessageBox.Show(parts(2))
maloprodaja.Items.Add(TileBarItem)
Right now i have to run function for each variable i have stored in global variables class, i would like to add all the results from first function to one array in Globals and then run second function with for each loop to populate my tilebar
Using an external variable like Globals to pass data around is extremely poor practice. Your first function in the question should return the data, or alternatively it should return the MySqlDataReader. That will simplify what you're trying to do later on and effectively make this problem go away.
I also so saw this:
Dim strQuery As String = "SELECT * FROM info.opcije_mp as mp inner join instalacije as i where mp.idopcije_mp =
i.opcijeMP and i.instalacije_hwid = '" + Globals.cpuid + "';"
I want to highlight this part:
" ... and i.instalacije_hwid = '" + Globals.cpuid + "';"
It's hard to understand just how bad that is. I can't think of a better way to get a program hacked. Google for parameterized queries and learn how to use them, rather than string concatentation, to put your cpuid into the sql statement.

Database values to array and join with VBscript

The values at the database look like 10000000,10000001,10000002. Now I have the value saved to a string (ID) and I want to assign a new value if its in and do a join so that it would come out as 0a,1a,2b.
Dim LineOfText
Dim i
Dim aryTextFile(12)
if ID.contains("10000000") then
aryTextFile(9) = "0a"
end if
if ID.contains("10000002") then
aryTextFile(10) = "1a"
end if
if ID.contains("10000001") then
aryTextFile(11) = "2b"
end if
LineOfText = String.Join(",", aryTextFile)
Trying this it gives me the error
Microsoft VBScript runtime error '800a01a8'
Object required: '10000001,10000002'
So what am I doing wrong?
After thinking some maybe I'm not making the value into a string right.
strSQL = "SELECT ID from table where hdnID like 3696 "
set objRec = Server.CreateObject("ADODB.Recordset")
objRec.Open strSQL, cn, 0, 1
Dim ID
ID = objRec("ID")
I have this working in C# but need a vbscript equivalent
The c# code is
string[] Otherspace = new string[] { 1a, 2b, 3c };
string txaNotes = String.Join(", ", Otherspace.Where(q => !string.IsNullOrEmpty(q)));
Try this:
LineOfText = join(aryTextFile, ",")
You can use a .Net import to build an Array, then joining it.
Example of how you can achieve this:
Dim DataList, ID
ID = "10000000 foo 10000002 bar"
Set DataList = CreateObject("System.Collections.ArrayList")
If instr(ID, "10000000") Then DataList.Add "0a"
If instr(ID, "10000001") Then DataList.Add "1a"
If instr(ID, "10000002") Then DataList.Add "2b"
LineOfText = join(DataList.ToArray, ", ")
' LineOfText will have value "0a, 2b"

Hashing gone wrong

I'm using the same function to hash values for comparison during login as I am to hash the passwords when users register:
Public Shared Function Compute(ByVal text As String, ByVal algorithm As String, Optional ByVal salt() As Byte = Nothing) As String
If salt Is Nothing Then
Dim saltSize As Integer = 8
salt = New Byte(saltSize - 1) {}
Dim rng As New RNGCryptoServiceProvider
rng.GetNonZeroBytes(salt)
End If
Dim textBytes As Byte() = Encoding.UTF8.GetBytes(text)
Dim saltedTextBytes() As Byte = New Byte(textBytes.Length + salt.Length - 1) {}
For i As Integer = 0 To textBytes.Length - 1
saltedTextBytes(i) = textBytes(i)
Next i
For i As Integer = 0 To salt.Length - 1
saltedTextBytes(textBytes.Length + i) = salt(i)
Next i
Dim hash As HashAlgorithm
If algorithm Is Nothing Then
algorithm = ""
End If
Select Case algorithm.ToUpper
Case "SHA1" : hash = New SHA1Managed
Case "SHA256" : hash = New SHA256Managed
Case "SHA384" : hash = New SHA384Managed
Case "SHA512" : hash = New SHA512Managed
Case Else : hash = New MD5CryptoServiceProvider
End Select
Dim hashBytes As Byte() = hash.ComputeHash(saltedTextBytes)
Dim saltedHash() As Byte = New Byte(hashBytes.Length + salt.Length - 1) {}
For i As Integer = 0 To hashBytes.Length - 1
saltedHash(i) = hashBytes(i)
Next i
For i As Integer = 0 To salt.Length - 1
saltedHash(hashBytes.Length + i) = salt(i)
Next i
Dim hashValue As String = Convert.ToBase64String(saltedHash)
Return Left(hashValue, 36)
End Function
My problem is that when I try to log in on an account whose password was hashed by this function, the hashed values don't match up. I think I'm skipping a step or something.
Here's the code for user account creation:
' The email address needs to be valid
Dim pattern As String = "^(?("")("".+?""#)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$"
Dim match As Match = Regex.Match(txtEmail.Text, pattern)
If match.Success Then
'Hash the user's password before entering it into the database.
Dim pass As String = Crypt.Compute(txtPass.Text, "SHA512", Nothing)
' Enter the information from the form into the database.
Dim sql As String = "INSERT INTO Users(Username, Password, EmailAddress) " & _
"VALUES(#User, #Pass, #Email)"
Dim cmd As New SqlCommand(sql, conn)
cmd.Parameters.AddWithValue("#User", txtName.Text)
cmd.Parameters.AddWithValue("#Pass", pass)
cmd.Parameters.AddWithValue("#Email", txtEmail.Text)
conn.Open()
cmd.ExecuteNonQuery()
conn.Close()
Else
lblError.Text = "Invalid email address. Please correct."
lblError.ForeColor = Drawing.Color.Red
End If
There are more checks that aren't included here that aren't relevant to my problem.
Here's my user login:
Dim pass As String = Crypt.Compute(txtPass.Text, "SHA512", Nothing)
Dim UserData As New DataSet
Dim UserAdapter As New SqlDataAdapter
UserAdapter.SelectCommand = New SqlCommand("SELECT * FROM Users " & _
"WHERE Username = #User AND Password = #Pass", conn)
UserAdapter.SelectCommand.Parameters.AddWithValue("#User", txtUser.Text)
UserAdapter.SelectCommand.Parameters.AddWithValue("#Pass", pass)
UserAdapter.Fill(UserData)
If UserData.Tables(0).Rows.Count <> 1 Then
lblError.Text = "Invalid username or password."
lblError.ForeColor = Drawing.Color.Red
Session("LoginAttempt") = CInt(Session("LoginAttempt")) + 1
Else
Session("LoggedIn") = True
Response.Redirect("Home.aspx")
End If
As far as I can see, there is no difference in the hashing I've done here.
Does anyone have any ideas?
When you creating an account by inserting into the table, you are using txtName.Text for the username, but when checking the credentials you are using txtUser.Text.
Why are you using a random salt? Doesn't the salt have to be the same for every encryption? I've pasted your code into a new project, and when I run the Compute method twice in a row for the same password, I get two different results... obviously that won't work. Try passing in a salt value instead of Nothing, and use the same salt for creating accounts and comparing login. Here's some sample code that works:
Dim thePass As String = "MyPassword"
Dim theSalt As String = "salt"
Dim pass As String = Compute(thePass, "SHA512", Encoding.UTF8.GetBytes(theSalt))
Console.WriteLine(pass)
Dim pass2 As String = Compute(thePass, "SHA512", Encoding.UTF8.GetBytes(theSalt))
Console.WriteLine(pass2) 'pass and pass2 are identical
Hope this helps!
Unless I'm missing it (not really familiar with the language), you don't store the salt anywhere.
You have to use the same salt you've used when creating the account for the verification.
On a side note: You can either generate a random salt for every user account or use a fixed salt for all accounts. Either method works. The first is theoretically more secure, but if the salt is long enough, both are fine for practical purposes.