Sql Statement Custom Class - sql

I am new to programming, I tried to create a sub that contains a SQL statement in it using parameterized query. My code is not working, there might something lacking to it or am I doing it wrong.
My connection string is in the app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name = "DBCS"
connectionString ="Data Source=Mic-PC\Developer; Initial Catalog=Customer; User Id = sa; Password=1224334;"
providerName ="System.Data.SqlClient" />
</connectionStrings>
</configuration>
I create a class called SELECTCLASS and here is the content
Imports System.Configuration
Imports System
Imports System.Threading.Tasks
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Data.SqlClient
Public Class SelectClass
Public Sub searchData(search As String, LastName As String, FirstName As String, MiddleInitial As String, Age As String, Address As String)
Dim CS As String = ConfigurationManager.ConnectionStrings("DBCS").ConnectionString
Using con As New SqlConnection(CS)
Dim cmd As SqlCommand = New SqlCommand("spGetCustomerByName", con)
cmd.CommandType = CommandType.Storedprocedure
cmd.Parameters.AddWithValue("#FirstName", "%" + search + "%")
con.Open()
Using rdr = cmd.ExecuteReader()
If rdr.HasRows Then
While rdr.Read()
LastName = rdr.GetString(1).ToString
FirstName = rdr.GetString(2).ToString
MiddleInitial = rdr.GetString(3).ToString
Age = rdr.GetString(4).ToString
Address = rdr.GetString(5).ToString
End While
End If
End Using
End Using
End Sub
End Class
And here is the code for my Windows Form:
Imports System.Configuration
Imports System
Imports System.Threading.Tasks
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Data.SqlClient
Public Class SELECTVB
Dim SelectData As New SelectClass
Private Sub cmdSearch_Click(sender As Object, e As EventArgs) Handles cmdSearch.Click
SelectData.searchData(txtsearch.Text, TextBox1.Text.ToString, TextBox2.Text.ToString, TextBox3.Text.ToString, TextBox4.Text.ToString, TextBox5.Text.ToString)
End Sub
Any help is greatly appreciated, thank you in advance!

I think I see two problem with your code, neither of which have anything to do with ADO or SQL. The problems are related to the way that the data that is read from the database is being passed back to the calling method. I suspect that the searchData method is successfully reading the data from the query results (though you would need to confirm this using the debugger by setting a break-point, stepping through the method, and inspecting the variables' values).
The first problem I see is that you are passing the result of the TextBox.Text.ToString method rather than the TextBox.Text property itself. In other words, you should be calling the searchData method like this:
Private Sub cmdSearch_Click(sender As Object, e As EventArgs) Handles cmdSearch.Click
SelectData.searchData(txtsearch.Text, TextBox1.Text, TextBox2.Text, TextBox3.Text, TextBox4.Text, TextBox5.Text)
End Sub
Secondly, you are attempting to reassign the reference of those Text arguments from inside the searchData method, but the parameters are not declared ByRef. If you want to do it that way, you would need to change the parameters of the method to ByRef, like this:
Public Sub searchData(search As String, ByRef LastName As String, ByRef FirstName As String, ByRef MiddleInitial As String, ByRef Age As String, ByRef Address As String)
However, none of that would be a problem if you returned the results via the function's return value rather than changing the values of the parameters. Most developers, including myself, discourage using ByRef parameters in this manner because it's unexpected behavior. Typically parameters should be reserved for inputs rather than outputs. For instance, if you created a class like this:
Public Class Customer
Public Property LastName As String
Public Property FirstName As String
Public Property MiddleInitial As String
Public Property Age As String
Public Property Address As String
End Class
Then you could change your search method to return a Customer object, like this:
Public Function SearchData(search As String) As Customer
Dim result As Customer = Nothing
Dim CS As String = ConfigurationManager.ConnectionStrings("DBCS").ConnectionString
Using con As New SqlConnection(CS)
Using cmd As SqlCommand = New SqlCommand("spGetCustomerByName", con)
cmd.CommandType = CommandType.Storedprocedure
cmd.Parameters.AddWithValue("#FirstName", "%" & search & "%")
con.Open()
Using rdr = cmd.ExecuteReader()
If rdr.HasRows Then
If rdr.Read() Then
result = New Customer()
result.LastName = rdr.GetString(1)
result.FirstName = rdr.GetString(2)
result.MiddleInitial = rdr.GetString(3)
result.Age = rdr.GetString(4)
result.Address = rdr.GetString(5)
End If
End If
End Using
End Using
End Using
Return result
End Sub
And then you could call it like this:
Private Sub cmdSearch_Click(sender As Object, e As EventArgs) Handles cmdSearch.Click
Dim c As Customer = SelectData.searchData(txtsearch.Text)
If c IsNot Nothing Then
TextBox1.Text = c.LastName
TextBox2.Text = c.FirstName
TextBox3.Text = c.MiddleInitial
TextBox4.Text = c.Age
TextBox5.Text = c.Address
End If
End Sub
However, given the context of what you are doing, it seems likely that your method ought to be returning a list of customers rather than just a single one.
If making these changes still does not fix the problem, stepping through the search method with the debugger will be your best chance of solving the problem. It could be that the stored procedure is returning no matches, or it's returning a different number of columns than you are expecting, or it could be returning a column with a data-type other than a string (like the age column might be an integer, for instance). It's impossible to say, given only the information that you've provided, what the precise problem with the data access code would be.

Related

Setting database class at run time in VB.Net

I have the follow classes for Sqlite and SqlServer:
Class for SQLite:
Imports System.Data.SQLite
Public Class clsOperDB_SQLite
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SQLiteConnection(strConn_SQLITE)
Using cmd As New SQLiteCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
Class for SQLSERVER:
Imports System.Data.SqlClient
Public Class clsOperDB_SQLSERVER
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SqlConnection(strConn_SQLSERVER)
Using cmd As New SqlCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
this is my test form:
Public Class Form1
'form level variable
Dim dbConnector
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim connectionType As String = "SQLITE"
' need something like this or any way to set form level variable
If connectionType = "SQLITE" Then
dbConnector = clsOperDB_SQLite
Else
dbConnector = clsOperDB_SQLSERVER
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'get value from SQLITE
Dim ValueFromDatabase As String = dbConnector.getValue("select .....")
End Sub
End Class
I need help to define dbConnector variable and set its value, also intellisense should show me class methods, using a parameter I want to change database and avoid using a conditional for every time I want to use one or the other database :
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Query As String = "Select ..."
Dim ValueFromDatabase As String = ""
' dont want to use if for each sql query
If connectionType = "SQLITE" Then
ValueFromDatabase = clsOperDB_SQLite.getValue(Query)
Else
ValueFromDatabase = clsOperDB_SQLSERVER.getValue(Query)
End If
End Sub
The rest of methods and params for both classes are the same only change class data objects (SQLiteConnection, SqlConnection, and so)
Thanks
You should define an interface that species all the common members. You can then create a class for each data source that implements that interface. In your application code, you can then declare a variable of that interface type and assign an instance of any class that implements it to that variable. You can then just use that variable and invoke any member of the interface without caring what type the actual class instance is.
The interface and the implementing classes would look something like this:
Public Interface IDataAccessProvider
Function GetValue(sql As String) As String
End Interface
Public Class SqliteDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQLite-specific implementation here.
End Function
End Class
Public Class SqlServerDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQL Server-specific implementation here.
End Function
End Class
Your application code might then look like this:
Private dataAccessProvider As IDataAccessProvider
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Read a value that identifies the data source and store it here.
'The value might be stored in the config file or wherever else is appropriate.
Dim dataSourceIdentifier As String
Select Case dataSourceIdentifier
Case "SQLite"
dataAccessProvider = New SqliteDataAccessProvider()
Case "SQL Server"
dataAccessProvider = New SqlServerDataAccessProvider()
End Select
End Sub
You can then just call dataAccessProvider.GetValue in your code without any care for what the data source actually is, except to ensure that your SQL syntax is valid for that data source.
Please note that, while what you do is up to you, I have chosen to use a sane naming convention in this code. No one would last long working for me using class names like clsOperDB_SQLSERVER. There's a reason you don't see any types with names like that in the .NET Framework.

Why i can not use string dataType as input argument in webmethod in vb.net

i implemented a webservice with vb.net
method is like this
Public Class WebService
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function INSERT_NEW(ByVal i As Integer, ByVal f As String) As String
Dim con As New OleDbConnection
Dim cmd As New OleDbCommand
Try
con.ConnectionString = ConfigurationManager.ConnectionStrings("WebConnectionSTR").ToString
'Dim strMdbPath As String = "C:\Users\Hossein\Documents\Visual Studio 2010\WebSites\WebSite1\"
'Dim strProvider As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
'Dim strDBFile As String = "db.mdb"
cmd.Connection = con
cmd.CommandText = "insert into tb values (" & i & ",'" & f & "')"
con.Open()
cmd.ExecuteNonQuery()
con.Close()
Return "1"
Catch ex As Exception
con.Close()
Return "0"
End Try
End Function
End Class
it works if i run it and invoke it
but when i create a windows application i occured with an unkown problem
because i used 2 (integer and string) input parameters in web method as input parameters,
INSERT_NEW(byval i as integer,byval f as string) as string
it didnt work
Imports wsdl.Myservice
Public Class Form1
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
Dim s As WebServiceSoap = New WebServiceSoapClient
lblAdd.Text = s.INSERT_NEW(txt1.Text, txt2.Text)
End Sub
End Class
but when i change the input argument in web method to INTEGER it works properly
is it a limitation to use data types in web method in web service OR i did something wrong???
i added these 3 photos to show you the exact error that i get.
You declare your webmethod to receive an Integer and a String. So you should pass an Integer and a String, but your code tries to pass two strings. You should respect the signature of the webmethod and pass the parameters as expected
lblAdd.Text = s.INSERT_NEW(Convert.ToInt32(txt1.Text), txt2.Text)
Of course, here I am assuming that the string in txt1.Text is convertible in an integer.
Said that I wish to point your attention to a very big problem of your code:
What happen if a malicious user pass for the parameter f the following string
"xxxxx');DELETE FROM tb; --"
It is called Sql Injection and could wreak havoc with your database. Try to use ALWAYS a parameterized query when you receieve input from your users and pass it to a database command
Using con = New OleDbConnection(ConfigurationManager.ConnectionStrings("WebConnectionSTR").ConnectionString)
Using cmd = New OleDbCommand("insert into tb values (?, ?)", con)
Try
con.Open()
cmd.Parameters.AddWithValue("#p1",i)
cmd.Parameters.AddWithValue("#p2",f)
cmd.ExecuteNonQuery()
Return "1"
Catch ex As Exception
Return "0"
End Try
End Using
End Using
Finally i found the answer myself
Imports wsdl.Myservice
Imports System.Reflection
Public Class Form1
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
Dim s As WebServiceSoap = New WebServiceSoapClient
Dim method As MethodInfo = s.GetType().GetMethod("INSERT_NEW")
Dim returnValue As Integer = method.Invoke(s, New Object() {CInt(txt1.Text), txt2.Text})
lblAdd.Text = returnValue
End Sub
End Class

Not Knowing How to Display Username After Login

I’m still stumbling with this page over and over again. Just couldn’t get the user’s Username (using email as the username) to display on mysupport.aspx page after she’s successfully logged in. The result should look like this with the email showing but it is not retrieving anything:
Email: barb#hotmail.com
Being an amateur, I know I’m missing a big piece of the puzzle but I don’t know what. Am I using the mailLBL.Text = User.Identity.Name.ToString() wrongly? Below are the code-behind:
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Data.SqlClient
Partial Class mysupport
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim sConnection As New SqlConnection(ConfigurationManager.ConnectionStrings("TrackTicketsConnectionString2").ConnectionString)
sConnection.Open()
Dim cmdS As String = "Select Email from Users Where Deleted='N'"
Dim cmdCheckmail As New SqlCommand(cmdS, sConnection)
If Session("Ticket") IsNot Nothing Then
mailLBL.Text = User.Identity.Name.ToString()
Else
Response.Redirect("SignIn.aspx")
End If
End Sub
Protected Sub signinBTN_Click(ByVal sender As Object, ByVal e As EventArgs)
Session("Ticket") = Nothing
Response.Redirect("SignIn.aspx")
End Sub
End Class
Any help and guidance is truly appreciated!
What you could do first is study these links:
How to: Implement Simple Forms Authentication
How to use Sessions
There are several things wrong with this code.
Old Code:
Dim cmdS As String = "Select Email from Users Where Deleted='N'"
Dim cmdCheckmail As New SqlCommand(cmdS, sConnection)
If Session("Ticket") IsNot Nothing Then
mailLBL.Text = User.Identity.Name.ToString()
Else
Response.Redirect("SignIn.aspx")
End If
Corrected Code:
If Session("Ticket") Is Nothing Then
Response.Redirect("SignIn.aspx")
Else
Dim cmdS As String = "Select Email from Users Where Deleted='N' AND Username=#Username"
Dim cmdCheckEmail as new SqlCommand(cmdS, sConnection)
cmd.AddParameters(new SqlParameter("#UserName", SqlDbType.VarChar).Value = Session("Ticket")
Dim obj as Object = cmd.ExecuteScalar()
If obj isNot Nothing
mailLBL.Text = Convert.ToString(obj)
End If
End
I hope that helps.

Cannot load temporary file in VBCodeProvider

Here's my script code:
Imports System.Diagnostics
Public Class Script
Implements IScript
Public Sub DoWork(w As WebBrowser, f As Form1) Implements IScript.DoWork
w.Navigate("http://www.google.com")
wait("5000")
w.Document.All("input").InvokeMember("click")
w.Document.All("input").SetAttribute("value", "Torrenter is the best!")
wait("2000")
w.Document.All("421").InvokeMember("click")
wait("1000")
End Sub
Public Sub wait(ByVal interval As Integer)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < interval
' Allows UI to remain responsive
Application.DoEvents()
Loop
sw.Stop()
End Sub
End Class
In-code:
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
If int1.Text = "1" Then
int1.Text = "0"
Dim script As IScript = GenerateScript(File.ReadAllText(ListBox2.Items.Item(int2).ToString()))
script.DoWork(WebBrowser1, Me) 'Object reference not set to an instance of an object.
int2 = int2 + 1
int1.Text = "1"
End If
End Sub
Why? :(
It's supposed to start the next script after the first was done. I tried 4 methods but I can't understand why.
The problem is that your script code is failing to compile, but then you are trying to instantiate an object from the compiled assembly anyway. Since it failed to compile, the assembly doesn't actually exist, hence the error. If you modify the Return line in the GenerateScript method, so that it shows the compile errors, the actual problem will be more clear:
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codes)
If results.Errors.HasErrors Then
Dim builder As New StringBuilder()
builder.AppendLine("Script failed to compile due to the following errors:")
For Each i As CompilerError In results.Errors
builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
builder.AppendLine()
Next
Throw New Exception(builder.ToString())
Else
Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
End If
I suspect that one of the reasons it's failing to compile is because the script uses IScript which is undefined. The reason it would complain that it's undefined is for two reasons. First, you declared the IScript interface as nested inside the Form1 class. You should move that outside of the form class so that it's not nested inside of any other type. Second, you are not specifying the full namespace nor importing the namespace in your script. You can automatically add the Imports line to the beginning of the script code before compiling it, like this:
Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codes As String = "Imports " & interfaceNamespace & Environment.NewLine & code
As I mentioned in the comments above, you really ought to be passing a string array into the CompileAssemblyFromSource method, not a string. I'm not sure how that even compiles, unless that's something having Option Strict Off somehow allows? In any case, it expects an array, so you should really be giving it one, like this:
Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codeArray() As String = New String() {"Imports " & interfaceNamespace & Environment.NewLine & code}
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeArray)
Another obvious reason why the script would fail to compile is because you have it using methods and properties of your Form1 class, as if it were a member of that class. Remember, the Script class defined by the script file source code is a completely separate class in a separate assembly. It will have no reference to the form unless you give it a reference, for instance, you could define the interface like this:
Public Interface IScript
Sub DoWork(f As Form1)
End Interface
Then, in your script, you could do this:
Public Class Script
Implements IScript
Public Sub DoWork(f As Form1) Implements IScript.DoWork
f.WebBrowser1.Navigate("http://www.google.com")
f.wait("5000")
f.wait("4000")
f.WebBrowser1.Document.All("input").InvokeMember("click")
f.WebBrowser1.Document.All("input").SetAttribute("value", "User")
f.wait("2000")
f.WebBrowser1.Document.All("421").InvokeMember("click")
End Sub
End Class
UPDATE
Ok, since you can't get it working, and I don't want this whole conversation to be a total loss, I put together a working project and tested it. Here's what you need to do to get it to work.
Contents of IScript.vb
Public Interface IScript
Sub DoWork(w As WebBrowser)
End Interface
Contents of Form1.vb
Imports Microsoft.VisualBasic
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.IO
Imports System.Text
Public Class Form1
Dim int1 As Integer = 0
Dim int2 As Integer = 0
Dim p As Point
Public Function GenerateScript(ByVal code As String) As IScript
Using provider As New VBCodeProvider()
Dim parameters As New CompilerParameters()
parameters.GenerateInMemory = True
parameters.ReferencedAssemblies.Add(GetType(WebBrowser).Assembly.Location)
parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
Dim interfaceNamespace As String = GetType(IScript).Namespace
code = "Imports System.Windows.Forms" & Environment.NewLine & "Imports " & interfaceNamespace & Environment.NewLine & code
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
If results.Errors.HasErrors Then
Dim builder As New StringBuilder()
builder.AppendLine("Script failed to compile due to the following errors:")
For Each i As CompilerError In results.Errors
builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
builder.AppendLine()
Next
Throw New Exception(builder.ToString())
Else
Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
End If
End Using
End Function
Public Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each File As FileInfo In New System.IO.DirectoryInfo(Application.StartupPath & "/scripts").GetFiles
If CheckedListBox1.GetItemCheckState(int2) = CheckState.Checked Then
ListBox1.Items.Add(File.FullName)
End If
int2 = int2 + 1
Next
int2 = 0
Dim script As IScript = GenerateScript(File.ReadAllText(ListBox1.Items.Item(int2).ToString()))
script.DoWork(WebBrowser1)
End Sub
End Class
Contents of script file
Imports System.Diagnostics
Public Class Script
Implements IScript
Public Sub DoWork(w As WebBrowser) Implements IScript.DoWork
w.Navigate("http://www.google.com")
wait("5000")
wait("4000")
w.Document.All("input").InvokeMember("click")
w.Document.All("input").SetAttribute("value", "User")
wait("2000")
w.Document.All("421").InvokeMember("click")
End Sub
Public Sub wait(ByVal interval As Integer)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < interval
' Allows UI to remain responsive
Application.DoEvents()
Loop
sw.Stop()
End Sub
End Class

SSIS script task component to write Input0Buffer to a text file

I'm trying to dump all input0Buffer to a txt file, to end my struggle with making the flat destination re-usable as i've over 100 package and each structure and columns are different.
I'm redirecting the error rows to a flat file, so it's a nightmare to set that manually in every package, so I wanna write the whole input without specifieng Row.Name, all of them into text file.
I'm up to the point that i'm getting only one column!! it's driving me crazy!!
Imports System
Imports System.Data
Imports System.Math
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
Imports System.IO
Imports System.Reflection
Imports System.Xml
Imports Microsoft.SqlServer.Dts.Pipeline
<Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute()> _
<CLSCompliant(False)> _
Public Class ScriptMain
Inherits UserComponent
Dim textWriter As StreamWriter
Private inputBuffer As PipelineBuffer
Public Overrides Sub ProcessInput(ByVal InputID As Integer, ByVal Buffer As Microsoft.SqlServer.Dts.Pipeline.PipelineBuffer)
inputBuffer = Buffer
MyBase.ProcessInput(InputID, Buffer)
End Sub
Public Overrides Sub PreExecute()
MyBase.PreExecute()
textWriter = New StreamWriter( "c:\Test4.txt", True)
End Sub
Public Overrides Sub PostExecute()
MyBase.PostExecute()
textWriter.Close()
''
End Sub
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim delim As String = ", "
Dim RowCount As Integer = 0
For RowCount = 0 To inputBuffer.ColumnCount = -1
If inputBuffer.Item(RowCount).ToString() = "" Then
inputBuffer.Item(RowCount) = String.Empty
End If
Next
textWriter.WriteLine(inputBuffer.Item(RowCount).ToString() & delim)
End Sub
End Class
can anyone help me please?
The issue is where your write is at. You are writing outside of your For loop. You loop through each row and set the value of the entry to String.Empty but you aren't doing any writing to the textWriter here. Adjust the code as follows:
Dim myBuilder As New StringBuilder
For RowCount = 0 To inputBuffer.ColumnCount = -1
If inputBuffer.Item(RowCount).ToString() = "" Then
inputBuffer.Item(RowCount) = String.Empty
End If
myBuilder.Append(inputBuffer.Item(RowCount).ToString() & delim)
Next
textWriter.WriteLine(myBuilder.ToString)
That will ensure that each column gets written. The only issue will be that your last column will have a delimiter after it. You might want to trim that off before you write it. Also, you will need to add an Imports System.Text to your code as well.
One note I wanted to add since it can cause some confusion: your loop counter is called RowCount when it is performing the act of counting columns (ColumnCount-1 shows us that). That might cause some confusion down the road and assumptions could be made that might cause coding mistakes.