Is this asynchronous programming correct ?
Since this is my first time using TAP, I want to make sure I do it correctly from the beginning.
I want to fill a table from a ODBC database and afterwards read some files and extract values out of it, without freezing my UI.
Why do I need to run OdbcDataAdapter and the file reading as tasks if I run the whole Function as a task in my UI Sub ? Otherwise it blocks my UI. thread.
UI Code
Private Async Sub frmOfsList_Shown(sender As Object, e As EventArgs) Handles MyBase.Show
Dim sw As New Stopwatch 'query time
sw.Start()
DataGridView1.Visible = False
Label2.Visible = False
DataGridView1.DataSource = Await OFS.GetJobList 'async method
sw.Stop()
Label2.Text = "Query time: " & sw.Elapsed.TotalSeconds & "s"
For i As Integer = 0 To DataGridView1.Rows.Count - 1 'color days until prodution date
If DataGridView1.Rows(i).Cells(3).Value < 0 Then
DataGridView1.Rows(i).Cells(3).Style.ForeColor = Color.Red
Else
DataGridView1.Rows(i).Cells(3).Style.ForeColor = Color.Green
End If
Next
DataGridView1.Visible = True 'show grid
DataGridView1.ClearSelection()
Label2.Visible = True
End Sub
Async Function
Public Shared Async Function GetJobList() As Task(Of DataTable)
Dim dq As Char = """"
Dim con As OdbcConnection = New OdbcConnection(constr)
con.Open()
'get data from OFS
Dim cmd As String = "SELECT p1.ProductionOrder, p1.Project, p1.ProductionDate, p1.Item, p1.Revision, p1.PlannedQty FROM " &
dq & "OFS460" & dq & "." & dq & "dbo" & dq & "." & dq & "tblProductionOrders" & dq & " p INNER JOIN " & dq & "OFS460" & dq & "." & dq & "dbo" &
dq & "." & dq & "tblProductionOrders" & dq & " p1 ON p.ProductionOrder = p1.ProductionOrder WHERE (p.Task=2820 AND p.StatusID=4) AND (p1.Task=2830 AND (p1.StatusID=1 OR p1.StatusID=2 OR p1.StatusID=3)) ORDER BY p1.ProductionDate"
Dim adapter As OdbcDataAdapter = New OdbcDataAdapter(cmd, con)
Dim datatable As New DataTable("JobList")
'fil table with job data async
Await Task.Factory.StartNew(Sub()
adapter.Fill(datatable)
End Sub)
'add columns to table
datatable.Columns.Add("Length", GetType(Double))
datatable.Columns.Add("Outside Dia", GetType(Double))
Dim proddate As DateTime
datatable.Columns.Add("Days until").SetOrdinal(3)
'calculate days
For j As Integer = 0 To datatable.Rows.Count - 1
proddate = datatable(j)(2)
datatable.Rows(j)(3) = proddate.Subtract(DateTime.Now).Days
Next
'Get length and diameter for each part
Dim searchpath As String = My.Settings.g250path
Await Task.Factory.StartNew(Sub()
Dim files As String()
Dim filetext As String
For i As Integer = 0 To datatable.Rows.Count - 1
files = System.IO.Directory.GetFiles(searchpath, "*" & datatable.Rows(i)("Item") & "*") 'get file by item#
If files.Length > 0 Then
filetext = System.IO.File.ReadAllText(files(0)) 'read file
datatable.Rows(i)("Length") = ProgramManager.GetValue(filetext, "I_R872", 7).ToString 'extract values
datatable.Rows(i)("Outside Dia") = ProgramManager.GetValue(filetext, "I_R877", 7).ToString
End If
Next i
End Sub)
Return datatable
End Function
You should not use Task.Factory.StartNew with Async-Await. You should use Task.Run, instead.
And you only need to get out of the UI thread once for the "heavy work" and return when done.
Try this:
Public Shared Function GetJobList() As DataTable
Dim dq As Char = """"
Dim con As OdbcConnection = New OdbcConnection(constr)
con.Open()
'get data from OFS
Dim cmd As String = "SELECT p1.ProductionOrder, p1.Project, p1.ProductionDate, p1.Item, p1.Revision, p1.PlannedQty FROM ""OFS460"".""dbo"".""tblProductionOrders"" p INNER JOIN ""OFS460"".""dbo"".""tblProductionOrders"" p1 ON p.ProductionOrder = p1.ProductionOrder WHERE (p.Task=2820 AND p.StatusID=4) AND (p1.Task=2830 AND (p1.StatusID=1 OR p1.StatusID=2 OR p1.StatusID=3)) ORDER BY p1.ProductionDate"
Dim adapter As OdbcDataAdapter = New OdbcDataAdapter(cmd, con)
Dim datatable As New DataTable("JobList")
'fil table with job data async
adapter.Fill(datatable)
'add columns to table
datatable.Columns.Add("Length", GetType(Double))
datatable.Columns.Add("Outside Dia", GetType(Double))
Dim proddate As DateTime
datatable.Columns.Add("Days until").SetOrdinal(3)
'calculate days
For j As Integer = 0 To datatable.Rows.Count - 1
proddate = datatable(j)(2)
datatable.Rows(j)(3) = proddate.Subtract(DateTime.Now).Days
Next
'Get length and diameter for each part
Dim searchpath As String = My.Settings.g250path
Dim files As String()
Dim filetext As String
For i As Integer = 0 To datatable.Rows.Count - 1
files = System.IO.Directory.GetFiles(searchpath, "*" & datatable.Rows(i)("Item") & "*") 'get file by item#
If files.Length > 0 Then
filetext = System.IO.File.ReadAllText(files(0)) 'read file
datatable.Rows(i)("Length") = ProgramManager.GetValue(filetext, "I_R872", 7).ToString 'extract values
datatable.Rows(i)("Outside Dia") = ProgramManager.GetValue(filetext, "I_R877", 7).ToString
End If
Next i
Return datatable
End Function
And invoke it like this:
DataGridView1.DataSource = Await Task.Run(AddressOf OFS.GetJobList1)
This way, the OFS.GetJobList1 function will be scheduled to execute on a thread pool thread and, when completed, the execution will resume on the caller sub/function and the return value of Task.Run (which is the return value of OFS.GetJobList1 wrapped with a Task(Of DataTable)) will be unwrapped and assigned to DataGridView1.DataSource.
I am reading csv file with oledb mechanism. My main issue is that the string values inside csv while reading are being trimmed (both: at the beggining and and the end with white spaces). I have some specific data in csv file which needs to have such white spaces in only some cases - that's why i cannot handle that after being processed. It has to be done with the convertion.
Unfortunatelly it has to be done with oledb and vb.net as our complex mechanism is based on those technologies.
Is that possible to find a hack or workaround that oledb will not trim my strings?
Below is my code, actual results and expected:
csv file:
Column1|Column2|Column3|Column4
Text1 | Text2| Text3 |Text4
schema.ini
[test.csv]
Format=Delimited(|)
Col1=Column1 Text
Col2=Column2 Text
Col3=Column3 Text
Col4=Column4 Text
Code
Private conn As New OleDbConnection
Private cmd As New OleDbCommand
Private myAccessDataReader As OleDb.OleDbDataReader = Nothing
Sub Main()
Try
Dim dirInfo As String = "C:\csv"
If conn.State = ConnectionState.Open Then
conn.Close()
End If
conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & dirInfo & ";Extended Properties=""Text;HDR=Yes;"";"
conn.Open()
cmd = New OleDbCommand("SELECT * From [test.csv]", conn)
myAccessDataReader = cmd.ExecuteReader()
If myAccessDataReader.HasRows Then
myAccessDataReader.Read()
End If
Console.WriteLine("|" + myAccessDataReader.Item("Column1") + "|")
Console.WriteLine("|" + myAccessDataReader.Item("Column2") + "|")
Console.WriteLine("|" + myAccessDataReader.Item("Column3") + "|")
Console.WriteLine("|" + myAccessDataReader.Item("Column4") + "|")
Console.ReadKey()
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub
Actual Results:
|Text1|
|Text2|
|Text3|
|Text4|
Expected Results:
|Text1 |
| Text2|
| Text3 |
|Text4|
Ps. I have tried with different settings in schema.ini: encoding, MaxScanRows, fixed width, but nothing helped.
I guess there is a general issue with trailing spaces when dealing with database: some char data types use spaces to fill the rest of the characters. For MSSql there is an option ANSI PADDING which you can turn ON/OFF, but I don't see a way to set that for Microsoft JET Engine which we use for CSV files; we support both oledb and odbc and this issue exists for both.
So, the answer is you can't. Trailing spaces will be always removed when you import data from a CSV data source, no matter if you define text/char/memo data type for your columns (e.g. using schema.ini) or enclose strings into double quotes. You can put some special character (non-space) in the end, after space(s), such as tab, for instance.
microsoft website
Try this out.....but there's no guarantee since I haven't put any error handling....
Function ReadCSVToTable(ByVal Schema As String) As DataTable
Dim file As New StreamReader("C:\dump\" & Schema)
Dim CSVName As String = file.ReadLine()
CSVName = Strings.Mid(CSVName, 2, CSVName.Length - 2)
Dim Delimiter As String = file.ReadLine
Delimiter = Strings.Mid(Delimiter, Strings.InStr(Delimiter, "(") + 1, Delimiter.Length - Strings.InStr(Delimiter, ")") + 1)
Dim Buffer As String = ""
Dim xtable As New DataTable
xtable.TableName = CSVName
'create table
Do
Buffer = file.ReadLine
Dim xCol As New DataColumn
With xCol
.ColumnName = Buffer.Split("=")(0)
.Caption = Buffer.Split("=")(1).Split(" ")(0)
Select Case Buffer.Split("=")(1).Split(" ")(1).ToLower
Case "text"
.DataType = GetType(String)
Case "integer"
.DataType = GetType(Integer)
Case "decimal"
.DataType = GetType(Decimal)
Case "boolean"
.DataType = GetType(Boolean)
Case Else
.DataType = GetType(String)
End Select
End With
xtable.Columns.Add(xCol)
Loop Until file.EndOfStream = True
file.Close()
file.Dispose()
'Fill the table
file = New StreamReader("C:\dump\" & CSVName)
'skip header
Buffer = file.ReadLine
Do
Buffer = file.ReadLine
Dim xCol(xtable.Columns.Count - 1)
Dim xCount As Integer = 0
For Each tCol As DataColumn In xtable.Columns
Select Case tCol.DataType
Case GetType(String)
xCol(xCount) = Convert.ToString(Buffer.Split(New String() {Delimiter}, StringSplitOptions.None)(xCount))
Case GetType(Integer)
xCol(xCount) = Convert.ToInt64(Buffer.Split(New String() {Delimiter}, StringSplitOptions.None)(xCount))
Case GetType(Decimal)
xCol(xCount) = Convert.ToDecimal(Buffer.Split(New String() {Delimiter}, StringSplitOptions.None)(xCount))
Case GetType(Boolean)
xCol(xCount) = Convert.ToBoolean(Buffer.Split(New String() {Delimiter}, StringSplitOptions.None)(xCount))
Case Else
xCol(xCount) = Convert.ToString(Buffer.Split(New String() {Delimiter}, StringSplitOptions.None)(xCount))
End Select
xCount = xCount + 1
Next
xtable.Rows.Add(xCol)
Loop Until file.EndOfStream = True
file.Close()
file.Dispose()
Return xtable
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim CSVTable As DataTable = ReadCSVToTable("schema.ini")
End Sub
I'm trying to make a program that downloads a bunch of domains and adds them windows hosts file but I'm having a bit of trouble. I keep getting an error when I try storing them in a list. I don't get why it doesn't work.
Sub Main()
Console.Title = "NoTrack blocklist to Windows Hosts File Converter"
Console.WriteLine("Downloading . . . ")
Dim FileDelete As String = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "/Downloads" & "/notracktemp.txt"
If System.IO.File.Exists(FileDelete) = True Then
System.IO.File.Delete(FileDelete)
End If
download()
Threading.Thread.Sleep(1000)
Dim s As New IO.StreamReader(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "/Downloads" & "/notracktemp.txt", True)
Dim tempRead As String ' = s.ReadLine
Dim tempSplit As String() ' = tempRead.Split(New Char() {" "})
Dim i As Integer = 0
Dim tempStore As String()
s.ReadLine()
s.ReadLine()
Do Until s.EndOfStream = True
tempRead = s.ReadLine
tempSplit = tempRead.Split(New Char() {" "})
Console.WriteLine(tempSplit(0))
tempStore(i) = tempSplit(0)'The part that gives me the error
i = i + 1
Loop
Console.ReadKey()
End Sub
Sub download()
Dim localDir As String = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
'"Enter file URL"
Dim url As String = "https://quidsup.net/notrack/blocklist.php?download"
'"Enter directory"
Dim dirr As String = localDir & "/Downloads" & "/notracktemp.txt"
My.Computer.Network.DownloadFile(url, dirr)
'System.IO.File.Delete(localDir & "/notracktemp.txt")
End Sub
tempStore() has to have a size
count number of lines in file with loop, then declare it as tempStore(i) where i is the amount of lines. Here is a function that counts the lines.
Function countlines()
Dim count As Integer
Dim s As New IO.StreamReader(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "/Downloads" & "/notracktemp.txt", True)
s.ReadLine()
s.ReadLine()
count = 0
Do Until s.EndOfStream = True
s.ReadLine()
count = count + 1
Loop
Console.WriteLine(count)
Return count
Console.ReadKey()
End Function
Then what you do is:
Dim count As Integer
count = countlines()
Dim tempStore(count) As String
I have a function SafeSqlLiteral, found online. It's a function to prevent SQL injection attacks. I want to use this function inside my SQL query like so:
Dim com As String = "SELECT * FROM Person WHERE " &
SafeSqlLiteral(SearchCriteria, 2)
SearchCriteria being user input. I want the user to be able to enter: strState LIKE 'M%' and show all the records matching a state like Maine. It feels like this would work. (SELECT * FROM Person WHERE strState LIKE 'M%')
I'm getting a whole bunch of errors (probably too many to post on here, but will post them if needed), and I'm having a hard time figuring out how to make my query work with the SafeSqlLiteral function.
Here is the SafeSqlLiteral function:
Public Function SafeSqlLiteral(theValue As System.Object,
theLevel As System.Object) As String
Dim strValue As String = DirectCast(theValue, String)
Dim intLevel As Integer = CInt(theLevel)
If strValue IsNot Nothing Then
If intLevel > 0 Then
strValue = strValue.Replace("'", "''")
strValue = strValue.Replace("--", "")
strValue = strValue.Replace("[", "[[]")
strValue = strValue.Replace("%", "[%]")
End If
If intLevel > 1 Then
Dim myArray As String() = New String() {"xp_ ", "update ", "insert ", "select ", "drop ", "alter ",
"create ", "rename ", "delete ", "replace "}
Dim i As Integer = 0
Dim i2 As Integer = 0
Dim intLenghtLeft As Integer = 0
For i = 0 To myArray.Length - 1
Dim strWord As String = myArray(i)
Dim rx As New Regex(strWord, RegexOptions.Compiled Or RegexOptions.IgnoreCase)
Dim matches As MatchCollection = rx.Matches(strValue)
i2 = 0
For Each match As Match In matches
Dim groups As GroupCollection = match.Groups
intLenghtLeft = groups(0).Index + myArray(i).Length + i2
strValue = strValue.Substring(0, intLenghtLeft - 1) + " " + strValue.Substring(strValue.Length - (strValue.Length - intLenghtLeft), strValue.Length - intLenghtLeft)
i2 += 5
Next
Next
End If
Return strValue
Else
Return strValue
End If
End Function
Thanks in advance for anyone helping me solve this problem! It's greatly appreciated!
I have such kind of data in a text file:
12343,M,Helen Beyer,92149999,21,F,10,F,F,T,T,T,F,F
54326,F,Donna Noble,92148888,19,M,99,T,F,T,F,T,F,T
99999,M,Ed Harrison,92147777,28,F,5,F,F,F,F,F,F,T
88886,F,Amy Pond,92146666,31,M,2,T,F,T,T,T,T,T
37378,F,Martha Jones,92144444,30,M,5,T,F,F,F,T,T,T
22444,M,Tom Scully,92145555,42,F,6,T,T,T,T,T,T,T
81184,F,Sarah Jane Smith,92143333,22,F,5,F,F,F,T,T,T,F
97539,M,Angus Harley,92142222,22,M,9,F,T,F,T,T,T,T
24686,F,Rose Tyler,92142222,22,M,5,F,F,F,T,T,T,F
11113,F,Jo Grant,92142222,22,M,5,F,F,F,T,T,T,F
I want to extract the Initial of the first name and complete surname. So the output should look like:
H. Beyer, M
D. Noble, F
E. Harrison, M
The problem is that I should not use String Split function. Instead I have to do it using any other way of string handling.
This is my code:
Public Sub btn_IniSurGen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_IniSurGen.Click
Dim vFileName As String = "C:\temp\members.txt"
Dim vText As String = String.Empty
If Not File.Exists(vFileName) Then
lbl_Output.Text = "The file " & vFileName & " does not exist"
Else
Dim rvSR As New IO.StreamReader(vFileName)
Do While rvSR.Peek <> -1
vText = rvSR.ReadLine() & vbNewLine
lbl_Output.Text += vText.Substring(8, 1)
Loop
rvSR.Close()
End If
End Sub
You can use the TextFieldParserClass. It will parse the file and return the results directly to you as a string array.
Using MyReader As New Microsoft.VisualBasic.FileIO.
TextFieldParser("c:\logs\bigfile")
MyReader.TextFieldType =
Microsoft.VisualBasic.FileIO.FieldType.Delimited
MyReader.Delimiters = New String() {","}
Dim currentRow As String()
'Loop through all of the fields in the file.
'If any lines are corrupt, report an error and continue parsing.
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
' Include code here to handle the row.
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
" is invalid. Skipping")
End Try
End While
End Using
For your wanted result, you may changed
lbl_Output.Text += vText.Substring(8, 1)
to
'declare this first
Dim sInit as String
Dim sName as String
sInit = vText.Substring(6, 1)
sName = ""
For x as Integer = 8 to vText.Length - 1
if vText.Substring(x) = "," Then Exit For
sName &= vText.Substring(x)
Next
lbl_Output.Text += sName & ", " & sInit
But better you have more than one lbl_Output ...
Something like this should work:
Dim lines As New List(Of String)
For Each s As String In File.ReadAllLines("textfile3.txt")
Dim temp As String = ""
s = s.Substring(s.IndexOf(","c) + 1)
temp = ", " + s.First
s = s.Substring(s.IndexOf(","c) + 1)
temp = s.First + ". " + s.Substring(s.IndexOf(" "c), s.IndexOf(","c) - s.IndexOf(" "c)) + temp
lines.Add(temp)
Next
The list Lines will contain the strings you need.