Null value of DataReader in List(of string) vb.net - arrays

I am writing a web service function in VB.net in a stored procedure in SQL server. I think the DataReader does not return any value.
Here is my stored procedure in Sql Server:
Create proc getVillageName
#village varchar(50)
as
Begin
SELECT
lookup_table.value FROM dbo.lookup_table INNER JOIN dbo.lookup_description ON
lookup_table.group_id = lookup_description.desc_id WHERE
lookup_description.description = 'Village' AND
lookup_table.value LIKE #village + '%'
End
Here is my code:
Public Function GetVillage(ByVal villageName As String) As List(Of String)
Dim strsql As String
Dim Villagevalue As List(Of String)
Dim param As SqlParameter
Dim conn As New SqlConnection With {.ConnectionString = "Server=MINTOY_DEV\MIGSSERVER;Database=SouthLinkDBO;User=sa;Pwd=123;"}
Dim cmd As New SqlCommand
Dim reader As SqlDataReader
Using conn
strsql = "getVillageName"
cmd = New SqlCommand(strsql, conn)
cmd.CommandType = CommandType.StoredProcedure
param = New SqlParameter("#village", villageName)
cmd.Parameters.Add(param)
conn.Open()
reader = cmd.ExecuteReader
While reader.Read
Villagevalue.Add(reader.Item("value").ToString)
End While
End Using
End Function

You're missing a line:
While reader.Read
Villagevalue.Add(reader.Item("value").ToString)
End While
End Using
Return Villagevalue '<-----------------
End Function
Are you looking at compiler warnings? VB will tell you if you forget a Return statement.
Better yet, open the Project Properties, click on the Compiler tab, and set "Function returning a reference type without a return value" to "Error".
Also, how is this working at all?
Dim Villagevalue As List(Of String)
That should be
Dim Villagevalue As New List(Of String)

Lots of things we can improve here:
Public Iterator Function GetVillage(ByVal villageName As String) As IEnumerable(Of String)
Using conn As New SqlConnection("Server=MINTOY_DEV\MIGSSERVER;Database=SouthLinkDBO;User=sa;Pwd=123;"), _
cmd As New SqlCommand("getVillageName", conn)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add("#village", SqlDbType.VarChar, 50).Value = villageName
conn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While reader.Read()
Yield DirectCast(reader("value"), String)
End While
End Using
End Using
End Function
The big thing is we actually return a value (Yield handles this for Iterator functions).
If you really need an actual List(Of String) (Hint: you usually don't; Your code will almost always perform a lot better if you arrange things to work with IEnumerable(Of T) for as long as possible) you can append a .ToList() when calling this method.

Related

"System.OutOfMemoryException: 'Out of memory.'" when reading image from SQL Server

I have images assigned to every button in my VB.NET form, the images come from SQL Server. The data type is varbinary(MAX).
This is my code:
Using con As New SqlConnection("con string")
Dim sql As String = "SELECT * FROM Inventory WHERE ID=#ID"
Using cmd As New SqlCommand(sql, con)
cmd.Parameters.Add("#ID", SqlDbType.VarChar).Value = 3
con.Open()
Using myreader As SqlDataReader = cmd.ExecuteReader()
If myreader.Read() AndAlso Not DBNull.Value.Equals(myreader("Image")) Then
Boton3.Text = myreader("Item")
Boton3.Enabled = myreader("ONOFF")
Dim ImgSql() As Byte = DirectCast(myreader("Image"), Byte())
Using ms As New MemoryStream(ImgSql)
Boton3.BackgroundImage = Image.FromStream(ms)
con.Close()
End Using
Else
Boton3.Text = myreader("Item")
Boton3.BackgroundImage = Nothing
Boton3.Enabled = myreader("ONOFF")
End If
End Using
End Using
End Using
The platform is 64bit. I'm thinking it might have to do with not disposing properly, but I'm not sure since I'm new to coding.
EDIT SHOWING NEW CODE AND HOW I RETRIVE MORE THAN ONE RECORD:
Private Sub Button12_Click(sender As Object, e As EventArgs) Handles Button12.Click
Dim dt As DataTable
Try
dt = GetInventoryDataByID(1)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
If dt.Rows.Count > 0 Then
Boton1.Text = dt.Rows(0)("Articulo").ToString
Boton1.Enabled = CBool(dt.Rows(0)("ONOFF"))
If Not DBNull.Value.Equals(dt.Rows(0)("Imagen")) Then
Dim ImgSql() As Byte = DirectCast(dt.Rows(0)("Imagen"), Byte())
Using ms As New MemoryStream(ImgSql)
Boton1.BackgroundImage = Image.FromStream(ms)
End Using
Else
Boton1.BackgroundImage = Nothing
End If
Else
MessageBox.Show("No records returned")
End If
Dim dt2 As DataTable
Try
dt2 = GetInventoryDataByID(2)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
If dt2.Rows.Count > 0 Then
Boton2.Text = dt2.Rows(0)("Articulo").ToString
Boton2.Enabled = CBool(dt2.Rows(0)("ONOFF"))
If Not DBNull.Value.Equals(dt2.Rows(0)("Imagen")) Then
Dim ImgSql() As Byte = DirectCast(dt2.Rows(0)("Imagen"), Byte())
Using ms As New MemoryStream(ImgSql)
Boton2.BackgroundImage = Image.FromStream(ms)
End Using
Else
Boton2.BackgroundImage = Nothing
End If
Else
MessageBox.Show("No records returned")
End If
End Sub
Private Function GetInventoryDataByID(id As Integer) As DataTable
Dim dt As New DataTable
Dim sql As String = "SELECT Imagen, Articulo, ONOFF FROM Inventario WHERE ID=#ID"
Using con As New SqlConnection("CON STRING"),
cmd As New SqlCommand(sql, con)
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = id
con.Open()
Using myreader As SqlDataReader = cmd.ExecuteReader()
dt.Load(myreader)
End Using
End Using
Return dt
End Function
End Class
You don't want to hold a connection open while you update the user interface. Separate you user interface code from your database code.
If you put a comma at the end of the first line of the outer Using block, both the command and the connection are included in same block. Saves a bit of indenting.
You are passing an integer to the #ID parameter but you have set the SqlDbType as a VarChar. Looks like a problem. I changed the SqlDbType to Int.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dt As DataTable
Try
dt = GetInventoryDataByID(3)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
If dt.Rows.Count > 0 Then
Boton3.Text = dt.Rows(0)("Item").ToString
Boton3.Enabled = CBool(dt.Rows(0)("ONOFF"))
If Not DBNull.Value.Equals(dt.Rows(0)("Image")) Then
Dim ImgSql() As Byte = DirectCast(dt.Rows(0)("Image"), Byte())
Using ms As New MemoryStream(ImgSql)
Boton3.BackgroundImage = Image.FromStream(ms)
End Using
Else
Boton3.BackgroundImage = Nothing
End If
Else
MessageBox.Show("No records returned")
End If
End Sub
Private Function GetInventoryDataByID(id As Integer) As DataTable
Dim dt As New DataTable
Dim sql As String = "SELECT * FROM Inventory WHERE ID=#ID"
Using con As New SqlConnection("con string"),
cmd As New SqlCommand(sql, con)
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = id
con.Open()
Using myreader As SqlDataReader = cmd.ExecuteReader()
dt.Load(myreader)
End Using
End Using
Return dt
End Function
EDIT Add Dispose on image
If Not DBNull.Value.Equals(dt.Rows(0)("Image")) Then
Dim ImgSql() As Byte = DirectCast(dt.Rows(0)("Image"), Byte())
Using ms As New MemoryStream(ImgSql)
If Boton3.BackgroundImage IsNot Nothing Then
Boton3.BackgroundImage.Dispose()
End If
Boton3.BackgroundImage = Image.FromStream(ms)
End Using
Else
If Boton3.BackgroundImage IsNot Nothing Then
Boton3.BackgroundImage.Dispose()
End If
End If
I resolved this issue by simply not using buttons. Instead I used pictureboxes as buttons and that resolved the issue. Im guesssing the problem is that buttons don't allow as much memory as pictureboxes.

MultipleActiveResultSets for SQL Server and VB.NET application

I am trying to get multiple data sets from SQL Server using a VB.NET application. The problem that every time I try to execute the query,
I get this message:
Cannot change property 'ConnectionString'. The current state of the connection is open
Then I tried to fix it by enabling MARS
<connectionStrings>
<add name="ConString"
providerName="System.Data.SqlClient"
connectionString="Data Source=my-PC;Initial Catalog=Project;Persist Security Info=True; MultipleActiveResultSets=true;User ID=user;Password=*****" />
</connectionStrings>
This is my code
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim obj, body
obj = TextBox1.Text
body = TextBox2.Text
For Each mail In getemail()
Send_mail(mail, obj, body, getattachment(mail))
Next
MsgBox("Traitement effectué")
End Sub
Function getemail() As List(Of String)
Dim strMailTo As New List(Of String)
Dim SQL As String = "Select EMail FROM [USER] WHERE EMail Is Not NULL And MatriculeSalarie Is Not NULL And [EMail] <> '' and EtatPaie = 3 and BulletinDematerialise = 1 "
Dim cmd As New SqlCommand
Dim sqLdr As SqlDataReader
Dim dr As DataRow
Try
ConnServer()
cmd.Connection = con
cmd.CommandText = SQL
Using sda As New SqlDataAdapter(cmd)
Using ds As New DataTable()
sda.Fill(ds)
sqLdr = cmd.ExecuteReader()
For i = 0 To ds.Rows.Count - 1
dr = ds.Rows(i)
strMailTo.Add(dr("EMail"))
Next
End Using
End Using
Return strMailTo
sqLdr.Close()
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
closeCon()
Return strMailTo
End Function
Function getattachment(email) As String()
Dim SQL As String = "Select MatriculeSalarie FROM [USER] WHERE [EMail]='" & email & "'"
Dim cmd As New SqlCommand
Dim sqLdr As SqlDataReader
ConnServer()
cmd.Connection = con
cmd.CommandText = SQL
Dim mat As String
mat = ""
Dim Dir As String = ConfigurationManager.AppSettings("path1").ToString
Dim file()
sqLdr = cmd.ExecuteReader()
While sqLdr.Read
mat = sqLdr.GetValue(sqLdr.GetOrdinal("MatriculeSalarie"))
End While
file = IO.Directory.GetFiles(Dir, mat.Substring(1) & "*.pdf")
sqLdr.Close()
Return file
End Function
If all you are going to do is show a message box in a Catch, don't do it in the database code. Let the error bubble up to the user interface code and put the Try around where the method is called.
Do not declare variables without a DataType. The button code with Option Infer on sets the type of obj and body.
Private ConStr As String = "Your connection string"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim obj = TextBox1.Text
Dim body = TextBox2.Text
Dim emails As New List(Of String)
Try
emails = getemail()
Catch ex As Exception
MessageBox.Show(ex.Message.ToString, "Error retrieving email list")
Exit Sub
End Try
For Each email In emails
Try
Send_mail(email, obj, body, getattachment(email))
Catch ex As Exception
MessageBox.Show(ex.Message, "Error getting attachments")
End Try
Next
MessageBox.Show("Traitement effectué")
End Sub
Parameters used by Sub and Function must have a DataType.
I don't know what you are doing here.
While sqLdr.Read
mat = sqLdr.GetValue(sqLdr.GetOrdinal("MatriculeSalarie"))
End While
Each iteration will overwrite the previous value of mat. I can only assume that you expect only a single value, in which case you can use ExecuteScalar to get the first column of the first row of the result set. Don't do anything with the data until after the connection is closed. Just get the raw data and close (End Using) the connection. Manipulate the data later.
Always use Parameters. Parameters are not treated as executable code by the database server. They are simply values. An example of executable code that could be inserted is "Drop table [USER];" where the value of a parameter belongs. Oops!
Function getemail() As List(Of String)
Dim SQL As String = "Select EMail FROM [USER]
WHERE EMail Is Not NULL
And MatriculeSalarie Is Not NULL
And [EMail] <> ''
And EtatPaie = 3
And BulletinDematerialise = 1;"
Dim dt As New DataTable
Using con As New SqlConnection("Your connection string"),
cmd As New SqlCommand(SQL, con)
con.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Dim strMailTo As New List(Of String)
strMailTo = (From row As DataRow In dt.AsEnumerable
Select row.Field(Of String)(0)).ToList
Return strMailTo
End Function
Function getattachment(email As String) As String()
Dim SQL As String = "Select MatriculeSalarie FROM [USER] WHERE [EMail]='" & email & "'"
Dim mat As String
Using con As New SqlConnection(ConStr),
cmd As New SqlCommand(SQL, con)
cmd.Parameters.Add("#email", SqlDbType.VarChar).Value = email
con.Open()
mat = cmd.ExecuteScalar().ToString()
End Using
Dim Dir As String = ConfigurationManager.AppSettings("path1").ToString
'Your original code was fine, no need for searchPattern.
'I added this so you could see if your search pattern was what you expected.
Dim searchPattern = mat.Substring(1) & "*.pdf"
Debug.Print(searchPattern) 'Appears in the Immediate window
Dim file = IO.Directory.GetFiles(Dir, searchPattern)
Return file
End Function

incorrect syntax near "=" in vb.net

I am a beginner and really need help. I want to display data from the database and assign the values to the textboxes and a combobox on a form, but I get this error
Incorrect syntax near "="
It appears is on this line
myreader = cmd.ExecuteReader
Please - any help?
Sub ref()
Dim conn As New SqlConnection
conn.ConnectionString = ("Data Source=.;Initial Catalog=UEW_ADMISSION_CHEAKER;Integrated Security=True")
conn.Open()
Dim strsql As String
strsql = "SELECT ProgName,MaleCuteOff,FemaleCutOff from CutOff_Point where ProgName=" + cmbCourse.SelectedItem + ""
Dim cmd As New SqlCommand(strsql, conn)
Dim myreader As SqlDataReader
myreader = cmd.ExecuteReader
myreader.Read()
txtFemale.Text = myreader("FemaleCutOff")
txtMale.Text = myreader("MaleCuteOff")
conn.Close()
End Sub
You should always use SQL parameters to pass parameters to SQL - it avoids embarrasing problems like single quotes breaking the query and deliberate SQL injection attacks.
It's probably best to make sure that there is a selected value before trying to use it.
Some things, e.g. database connections, use "unmanaged resources" and it is necessary to use the Dispose() method to make sure that things are cleaned up afterwards. The Using statement is a convenient way to get the computer to take care of that for you.
I didn't see a need for the query to return the value that was passed to it (ProgName).
You will need to adjust the .SqlDbType and .Size to match the database column.
Option Strict On
' ... other code
Sub Ref()
If cmbCourse.SelectedIndex >= 0 Then
Dim sql As String = "SELECT MaleCuteOff, FemaleCutOff FROM CutOff_Point WHERE ProgName = #ProgName"
Dim connStr = "Data Source=.\;Initial Catalog=UEW_ADMISSION_CHEAKER;Integrated Security=True"
Using conn As New SqlConnection(connStr),
cmd As New SqlCommand(sql, conn)
cmd.Parameters.Add(New SqlParameter With {.ParameterName = "#ProgName", .SqlDbType = SqlDbType.VarChar, .Size = 99, .Value = cmbCourse.SelectedItem})
conn.Open()
Dim rdr As SqlDataReader = cmd.ExecuteReader()
If rdr.HasRows Then
rdr.Read()
txtFemale.Text = rdr.GetInt32(0).ToString()
txtMale.Text = rdr.GetInt32(1).ToString()
End If
End Using
End If
End Sub
P.S. Shouldn't UEW_ADMISSION_CHEAKER be UEW_ADMISSION_CHECKER? It's best to have things spelt correctly as it is easier to type them.
First of all this Block of Code is not OK. You could use :
Using....End Using Method.
SqlCommand.Parameters Property for security issues.
Connection Strings and Configuration Files for security issues.
Allow me to rewrite your Code using the above methods.
Private Sub RetrieveAndDisplayCutOff()
Dim sbMale As New StringBuilder
Dim sbFemale As New StringBuilder
Dim strsql As String =
"SELECT MaleCutOff,FemaleCutOff FROM CutOff_Point WHERE ProgName = #ComboItem"
Using conn As New SqlConnection("Data Source=.;Initial Catalog=UEW_ADMISSION_CHEAKER;Integrated Security=True"),
CMD As New SqlCommand(strsql, conn)
CMD.Parameters.Add("#ComboItem", SqlDbType.VarChar).Value = ComboBox1.SelectedItem.ToString
conn.Open()
Using MyReader As SqlDataReader = CMD.ExecuteReader
While MyReader.Read 'Returns False if no more rows
'OP mentioned in comments that these fields were int
sbMale.AppendLine(MyReader.GetInt32(0).ToString)
sbFemale.AppendLine(MyReader.GetInt32(1).ToString)
End While
End Using
End Using
txtMale.Text = sbMale.ToString
txtFemale.Text = sbFemale.ToString
End Sub

Error: Fill: selectcommand.connection property has not been

I'm trying to retrieve binary data from a database.
I got this error: "Error: Fill: selectcommand.connection property has not been". I can't locate the error.
Public Shared Function BinaryData(ByVal sFileName As String) As Byte()
Dim strSql As String
Dim binaryFile As Byte() = Nothing
Dim dt As DataTable
Dim myCommand As New SqlCommand
Dim sqlConn As New SqlConnection
sqlConn = New SqlConnection("Data Source=xxx;Initial Catalog=xx;Persist Security Info=True;User ID=wxx;Password=xx;MultipleActiveResultSets=True;Application Name=EntityFramework")
sqlConn.Open()
myCommand.Connection = sqlConn
strSql = "SELECT Data FROM tbldrive WHERE Filename = '" + sFileName + "'"
Dim scmd As New SqlCommand(strSql, sqlConn)
dt = DataComponent.DataTableQuery(DataComponent.SqlConn, strSql)
If dt.Rows.Count > 0 Then
Try
binaryFile = DirectCast(dt.Rows(0).Item("Data"), Byte())
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
Return binaryFile
End Function
It looks like you've tried a few things in that code but accidentally left the remains of some attempts in there.
There are some things you could do a bit differently: as you're only after one item from the database, you can use ExecuteScalar; and when the code has finished with the SQL connection and command, they should have .Dispose() called on them - the Using statement will take care of that for you even if something goes wrong. Finally, you should always use SQL parameters to pass parameters to an SQL query - it makes it more secure and avoids having to worry about things like apostrophes in the value.
Public Shared Function BinaryData(ByVal sFileName As String) As Byte()
Dim sql As String = "SELECT Data FROM tbldrive WHERE Filename = #fname"
Dim connStr = "Data Source=xxx;Initial Catalog=xx;Persist Security Info=True;User ID=wxx;Password=xx;MultipleActiveResultSets=True;Application Name=EntityFramework"
Dim binaryFile As Byte() = Nothing
Using conn As New SqlConnection(connStr),
cmd As New SqlCommand(sql, conn)
cmd.Parameters.Add(New SqlParameter With {
.ParameterName = "#fname",
.SqlDbType = SqlDbType.NVarChar,
.Size = 255,
.Value = sFileName})
conn.Open()
Dim obj As Object = cmd.ExecuteScalar()
If obj IsNot Nothing Then
Try
binaryFile = DirectCast(obj, Byte())
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
End Using
Return binaryFile
End Function
(You may need to adjust the .SqlDbType and .Size parameters: they need to match the column type and size in the database. Also, you probably don't need MultipleActiveResultSets.)
The problem seems to be that you have two SqlCommand objects:
Dim myCommand As New SqlCommand
...
myCommand.Connection = sqlConn
It's assigned but not used.
Then you have defined another one:
Dim scmd As New SqlCommand(strSql, sqlConn)
that is not used either.
And I don't know why you have this:
dt = DataComponent.DataTableQuery(DataComponent.SqlConn, strSql)
Do you even need a SqlCommand if you are not using it ?
Clean up your code by removing unused variables.

SQL Server cursor to vb.net

everyone, How can I read a cursor from a stored procedure?
This is my code, but it's not working
Dim con As New SqlConnection(ConfigurationManager.ConnectionStrings("conexionBD").ToString())
Public Function LoadData() As String
Dim strQuery As String
strQuery = "Kardex_P''"
Using (con)
Dim sqlComm As SqlCommand = New SqlCommand(strQuery, con)
con.Open()
Dim sqlReader As SqlDataReader = sqlComm.ExecuteReader()
If sqlReader.HasRows Then
While (sqlReader.Read())
txtReporte = sqlReader.ToString
End While
End If
End Using
Return txtReporte
End Function
Please I need your help.
Thanks in advance
The cursor code will live in the stored procedure that ultimately returns a set of rows to read. Reference each column by ordinal value or name:
txtReporte.Text = sqlReader(0).ToString()
or
txtReporte.Text = sqlReader("YourColumnName").ToString()
What is wrong in OP code and how to fix it.
Dim con As New SqlConnection(ConfigurationManager.ConnectionStrings("conexionBD").ConnectionString
''//.ToString()) wrong but not fatal
Public Function LoadData() As String
Dim txtReporte as String = "" ''//you return this so declare
Dim strQuery As String
strQuery = "Kardex_P" ''//"Kardex_P''" This supposed to be SP name. What '' do inside?
Using (con) ''//this is good
Dim sqlComm As SqlCommand = New SqlCommand(strQuery, con)
''//By default command type is text so next line is critical
sqlComm.CommandType = CommandType.StoredProcedure
con.Open()
Dim sqlReader As SqlDataReader = sqlComm.ExecuteReader()
''//If sqlReader.HasRows Then checking "HasRows is redundant here
While (sqlReader.Read())
''//txtReporte = sqlReader.ToString no comment
txtReporte &= sqlReader("SomeField")
End While
''//End If
con.Close() ''//cleanup recommended
End Using
Return txtReporte
End Function
I hope this will work. Not perfect of course.

Resources