TransactionScope committing on each loop - vb.net

Please see the code below:
Private Sub TransactionExample3()
Dim objDR As SqlDataReader
Dim objCommand As SqlCommand, objCommand2 As SqlCommand
Dim objCon As SqlConnection
Dim objCon2 As SqlConnection
Dim id As Integer
Dim list As List(Of Integer) = New List(Of Integer)
Try
_ConString = "Data Source=databaseserver;Initial Catalog=Person;User ID=username;Password=password;MultipleActiveResultSets=True"
list.Add(1)
list.Add(2)
list.Add(3)
For Each i As Integer In list
Using trans = New TransactionScope()
objCon2 = New SqlConnection(_ConString)
objCon2.Open()
objCommand2 = New SqlCommand()
objCommand2.Connection = objCon2
Using objCon2
objCommand2.CommandText = "UPDATE Person SET forenames = #forenames WHERE " & _
" Reference = #Reference "
objCommand2.Parameters.AddWithValue("#forenames", i + 1)
objCommand2.Parameters.AddWithValue("#Reference", i)
objCommand2.ExecuteNonQuery()
objCommand2.Parameters.Clear()
End Using
trans.Complete()
End Using
Next
Catch ex As Exception
Throw
Finally
End Try
End Sub
This code works i.e. on each loop the changes are committed to the database.
Now please see the code below:
Private Sub TransactionExample3()
Dim objDR As SqlDataReader
Dim objCommand As SqlCommand, objCommand2 As SqlCommand
Dim objCon As SqlConnection
Dim objCon2 As SqlConnection
Dim id As Integer
Try
_ConString = "Data Source=server;Initial Catalog=Person;User ID=Username;Password=Password;MultipleActiveResultSets=True"
objCon = New SqlConnection(_ConString)
objCommand = New SqlCommand("SELECT top 10 * from Person")
objCommand.Connection = objCon
objCon.Open()
objDR = objCommand.ExecuteReader()
Do While objDR.Read
objCon2 = New SqlConnection(_ConString)
objCon2.Open()
Using trans = New TransactionScope()
objCommand2 = New SqlCommand()
objCommand2.Connection = objCon
Using objCon2
objCommand2.CommandText = "UPDATE Person SET forenames = #forenames WHERE " & _
" Reference = #Reference "
objCommand2.Parameters.AddWithValue("#forenames", objDR("Reference") + 10)
objCommand2.Parameters.AddWithValue("#Reference", objDR("Reference"))
objCommand2.ExecuteNonQuery()
objCommand2.Parameters.Clear()
End Using
End Using
Loop
objDR.Close() 'line 16
Catch ex As Exception
Throw
Finally
End Try
End Sub
In the second code exherpt, the scope is not complete (scope.complete), however the results are still committed to the database on each iteration of the while loop. Why is this?

In the first loop the opening of the TransactionScope is before the opening of the connection. In the second one is after. The connection is not enlisted in the Transaction and thus every command executes without being held by a transaction.
Try to switch these lines
Do While objDR.Read
Using trans = New TransactionScope()
objCon2 = New SqlConnection(_ConString)
objCon2.Open()
.....
Now you need the call to trans.Complete()
Private Sub TransactionExample3()
Dim objDR As SqlDataReader
Dim objCommand As SqlCommand, objCommand2 As SqlCommand
Dim objCon As SqlConnection
Dim objCon2 As SqlConnection
Dim id As Integer
_ConString = "Data Source=server;Initial Catalog=Person;User ID=Username;Password=Password;MultipleActiveResultSets=True"
Using objCon = New SqlConnection(_ConString)
objCommand = New SqlCommand("SELECT top 10 * from Person")
objCommand.Connection = objCon
objCon.Open()
objDR = objCommand.ExecuteReader()
Using trans = New TransactionScope()
Using objCon2 = New SqlConnection(_ConString)
objCon2.Open()
Do While objDR.Read
objCommand2 = New SqlCommand()
objCommand2.Connection = objCon
Using objCon2
objCommand2.CommandText = "UPDATE Person SET forenames = #forenames WHERE " & _
" Reference = #Reference "
objCommand2.Parameters.AddWithValue("#forenames", objDR("Reference") + 10)
objCommand2.Parameters.AddWithValue("#Reference", objDR("Reference"))
objCommand2.ExecuteNonQuery()
objCommand2.Parameters.Clear()
End Using
Loop
objDR.Close() 'line 16
End Using
trans.Complete()
End Using
End Using
End Sub
I suggest to move the Transaction and Connection opening outside the loop and call the Complete and destroy the connection after the foreach loop, if I understand your code correctly you update one record at each loop and so the Transaction makes sense only if you want to update all of your records or none. Another minor optimization could be to move the creation of the SqlCommand and the Parameters outside the loop. You update just the parameters value inside the loop without destroying and rebuilding the parameter collection at each loop

Related

Why is my VB program throwing an exception complaining that a OleDbDataReader is closed, when it should absolutely be open?

I am trying to use OleDB and SQLBulkCopy to pull data off an excel spreadsheet, into an SQL database. When I try and run my code, I get the following exception, triggered by
"bulkCopy.WriteToServer(objDR)" (which is also described in the question title): exception data
OleDB successfully reads the excel sheet which is then populated properly in the DataGridView I created within a WinForm for debugging purposes.
Below is a snippet of the relevant code.
Dim ExcelConnection As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\damon\Everyone\sheet1erictest.xlsx;Extended Properties=""Excel 12.0 Xml;HDR=Yes""")
ExcelConnection.Open()
Dim sheet As String = "Sheet5$"
Dim expr As String = "SELECT * FROM [" + sheet + "]"
Dim objCmdSelect As OleDbCommand = New OleDbCommand(expr, ExcelConnection)
Dim objDR As OleDbDataReader
Dim SQLconn As New SqlConnection()
Dim ConnString As String = "SERVER=SqlDEV;DATABASE=Freight;Integrated Security=True"
SQLconn.ConnectionString = ConnString
SQLconn.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(SQLconn)
bulkCopy.DestinationTableName = "dbo.test"
Try
objDR = objCmdSelect.ExecuteReader
Dim dt = New DataTable()
dt.Load(objDR)
DataGridView1.AutoGenerateColumns = True
DataGridView1.DataSource = dt
DataGridView1.Refresh()
bulkCopy.WriteToServer(objDR)
objDR.Close()
SQLconn.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Sub
Your reader object is being used by the DataTable, so comment out those lines and run them separately later with a new instance:
objDR = objCmdSelect.ExecuteReader
'Dim dt = New DataTable()
'dt.Load(objDR)
'DataGridView1.AutoGenerateColumns = True
'DataGridView1.DataSource = dt
'DataGridView1.Refresh()
bulkCopy.WriteToServer(objDR)

Error BC30616: Variable 'DBConnect' hides a variable in an enclosing block

I've adjusted some code which was written for me.. It sends out emails to my website members. I have added a part which selects items for sale on the site and puts them in the email (Featured items). However, I'm getting this error:
D:\domains\example.com\httpdocs\sendFeaturedEmail.aspx.vb(79): error
BC30616: Variable 'DBConnect' hides a variable in an enclosing block.
Can anyone please show me how the code should be changed to work? I'm a fairly new programmer.. I'm learning by changing code that was written for me. Many Thanks for any help at all.
Public Sub SelectEmailDetails()
Dim DBConnect As New DBConn
Dim conn As SqlConnection = DBConnect.Conn("DBConnectionString")
Dim cmd As SqlCommand = DBConnect.Command(conn, "SelectEmailDetails")
conn.Open()
Dim DR As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
Dim counter As Integer = 0
While DR.Read
'While DR.Read And counter < 1
Dim txtSubject As TextBox = fmvEmail.FindControl("txtSubject")
Dim txtEmailReminderText As TextBox = fmvEmail.FindControl("txtEmailReminderText")
Dim mm As New MailMessage
mm.To.Add(New MailAddress(DR("emailAddress").ToString))
mm.From = New MailAddress("admin#example.com", "example.com")
mm.IsBodyHtml = True
mm.Subject = txtSubject.Text
Dim bodyText As New StringBuilder
bodyText.AppendLine("<html><head><style>body { font-family:tahoma; font-size:1.0em; }</style></head><body>")
bodyText.AppendLine("<p><a href='http://example.com'><img src='http://example.com/files/images/emailLogo.png' border='0' /></a></p>")
bodyText.AppendLine("<p>Hurry.. Find these and more bargains in our current sale before they're gone..</p>")
Dim DBConnect As New DBConn
Using db As DbConnection = DBConnect.Conn("DBConnectionString")
Dim cmd As SqlCommand = DBConnect.Command(db, "FeaturedEmail")
cmd.Parameters.Add(New SqlParameter("MyDate", SqlDbType.Date, ParameterDirection.Input)).Value = MySale.prevDOW(DayOfWeek.Sunday).ToString("d")
db.Open()
Dim DR As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
ltlItems.Text = ltlItems.Text & "<div style='width:90%; word-wrap: break-word; margin:0 auto;'>"
While DR.Read
Dim friendlyOrderID = DR("link")
Dim strText = "http://www.example.com/catalog/images/thumbnails/" & DR("strText")
bodyText.AppendLine("<a href='" & friendlyOrderID & "'><img src='" & strText & "' /></a>")
End While
cmd.Dispose()
cmd = Nothing
db.Dispose()
db.Close()
End Using
bodyText.AppendLine("</body></html>")
mm.Body = bodyText.ToString
Dim smtp As New SmtpClient
smtp.Send(mm)
counter = counter + 1
End While
cmd.Dispose()
cmd = Nothing
conn.Close()
conn = Nothing
End Sub

Sending updates from datagridview to existing row in database

I am retrieving records from a database with the following code.
Dim SearchID As Integer = teacherID
Dim NewStudentID As Integer = studentID
Dim DisplayTable As New DataTable()
Dim da As New OleDbDataAdapter()
Dim sqlquery As String = ("select * from tblAppointments WHERE TeacherID =" & teacherID & "")
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
Try
da.SelectCommand = New OleDbCommand(sqlquery, conn)
da.Fill(Finalds, "Display")
DisplayTable = Finalds.Tables("Display")
DisplayTable.Columns.Remove("Instrument")
DisplayTable.Columns.Remove("Room")
DisplayTable.Columns.Remove("TeacherID")
Registersgridview.DataSource = DisplayTable
Registersgridview.Columns(0).Visible = False
conn.Close()
Catch ex As Exception
MsgBox("There are no appointments in the database for " + Tutorcombox.Text)
End Try
It also there then added to a datagridview and certain columns are removed and some are hidden aswell.
Because its essentially a register, when the use clicks on the datagridview field that is a boolean it changes from false to true. I have been trying to send this back to the database, but have had no luck. I have tried the following :
Dim dt As DataTable = New DataTable("SendTable")
Dim row As DataRow
dt.Columns.Add("appID", Type.GetType("System.Int32"))
dt.Columns.Add("Present", Type.GetType("System.Boolean"))
For i = 0 To Registersgridview.Rows.Count - 1
row = dt.Rows.Add
row.Item("appID") = Registersgridview.Rows(i).Cells(0)
row.Item("Present") = Registersgridview.Rows(i).Cells(5)
Next
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
Dim sqlquery As String = "Update tblAppointments SET Present = #Present WHERE appID = #appID"
Dim sqlcommand As New OleDbCommand
For Each newrow As DataRow In dt.Rows
With sqlcommand
.CommandText = sqlquery
.Parameters.AddWithValue("#Present", newrow.Item(1))
.Parameters.AddWithValue("#appID", newrow.Item(0))
.ExecuteNonQuery()
End With
conn.Close()
Next
But have had no luck with doing so, as it crashes without an error.
Can anyone help?
I solved the problem myself, if any of you are having similar problems here is the solution
Dim dt As DataTable = New DataTable("SendTable")
Dim row As DataRow
dt.Columns.Add("appID", Type.GetType("System.Int32"))
dt.Columns.Add("Present", Type.GetType("System.Boolean"))
For i = 0 To Registersgridview.Rows.Count - 1
Dim appID As Integer = Registersgridview.Rows(i).Cells(0).Value
Dim present As Boolean = Registersgridview.Rows(i).Cells(4).Value
row = dt.Rows.Add
row.Item("appID") = appID
row.Item("Present") = present
Next
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
Dim sqlquery As String = "UPDATE tblAppointments SET Present = #Present WHERE appID = #appID"
Dim sqlcommand As New OleDbCommand
For Each newrow As DataRow In dt.Rows
With sqlcommand
.CommandText = sqlquery
.Parameters.AddWithValue("#Present", newrow.Item(1))
.Parameters.AddWithValue("#appID", newrow.Item(0))
.Connection = conn
.ExecuteNonQuery()
End With
Next
conn.Close()
Registersgridview.DataSource = Nothing
dt.Clear()
try this:
Dim dt As DataTable = New DataTable("SendTable")
Dim row As DataRow
dt.Columns.Add("appID", Type.GetType("System.Int32"))
dt.Columns.Add("Present", Type.GetType("System.Boolean"))
For i = 0 To Registersgridview.Rows.Count - 1
row = dt.Rows.Add
row.Item("appID") = Registersgridview.Rows(i).Cells(0)
row.Item("Present") = Registersgridview.Rows(i).Cells(5)
Next
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
Dim sqlquery As String = "Update tblAppointments SET Present = #Present WHERE appID = #appID"
Dim sqlcommand As New OleDbCommand
For Each newrow As DataRow In dt.Rows
With sqlcommand
.CommandText = sqlquery
.Parameters.AddWithValue("#Present", newrow.Item(5))
.Parameters.AddWithValue("#appID", newrow.Item(0))
.ExecuteNonQuery()
End With
conn.Close()
Next

Condition or Rule when Importing Excel data to SQL

At line 4 of my code, how or is it possible to have a condition in there?
For example:
Dim expr As String = "SELECT * FROM [Sheet1$] WHERE excelColumn1 <> NULL"
I have tried it, but it gives an Error: No value given for one or more required parameters.
Dim ExcelConnection As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\z.fontanilla\Documents\etl2.xlsx;Extended Properties=""Excel 12.0 Xml;HDR=Yes""")
ExcelConnection.Open()
Dim expr As String = "SELECT * FROM [Sheet1$]"
Dim objCmdSelect As OleDbCommand = New OleDbCommand(expr, ExcelConnection)
Dim objDR As OleDbDataReader
Dim SQLconn As New SqlConnection()
Dim ConnString As String = "Data Source=cyayay\sqlexpress;Initial Catalog=reportingDB;Integrated Security=True"
SQLconn.ConnectionString = ConnString
SQLconn.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(SQLconn)
bulkCopy.DestinationTableName = "tFalse"
Try
objDR = objCmdSelect.ExecuteReader
bulkCopy.BatchSize = 5000
bulkCopy.WriteToServer(objDR)
objDR.Close()
SQLconn.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
ExcelConnection.Close()

Using SQLDataReader instead of recordset

I am new to this and had this question. Can I use SQLDataReader instead of a Recordset. I want to achieve the following result in an SQLDataReader.
Dim dbConn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim sqlstr As String = "SELECT Name,Status FROM table1 WHERE id=" + item_id.Value.ToString
rs.Open(SQL, dbConn)
While Not rs.EOF
txtName.Text = rs.Fields.Item("Name").Value
ddlstatus.SelectedIndex = 1
rs.MoveNext()
End While
rs.Close()
rs = Nothing
dbConn.Close()
dbConn = Nothing
Can I replace recordset with SQLDataReader and if I can can you please show me the changes in code?
Its highly recommend that you use the using pattern:
Dim sConnection As String = "server=(local);uid=sa;pwd=PassWord;database=DatabaseName"
Using Con As New SqlConnection(sConnection)
Con.Open()
Using Com As New SqlCommand("Select * From tablename", Con)
Using RDR = Com.ExecuteReader()
If RDR.HasRows Then
Do While RDR.Read
txtName.Text = RDR.Item("Name").ToString()
Loop
End If
End Using
End Using
Con.Close()
End Using
You will have to swap out a few things, something similar to the following.
Here is an example, you will need to modify this to meet your goal, but this shows the difference.
I also recommend using a "Using" statement to manage the connection/reader. Also, a parameterized query.
Dim sConnection As String = "server=(local);uid=sa;pwd=PassWord;database=DatabaseName"
Dim objCommand As New SqlCommand
objCommand.CommandText = "Select * From tablename"
objCommand.Connection = New SqlConnection(sConnection)
objCommand.Connection.Open()
Dim objDataReader As SqlDataReader = objCommand.ExecuteReader()
If objDataReader.HasRows Then
Do While objDataReader.Read()
Console.WriteLine(" Your name is: " & Convert.ToString(objDataReader(0)))
Loop
Else
Console.WriteLine("No rows returned.")
End If
objDataReader.Close()
objCommand.Dispose()
Dim rdrDataReader As SqlClient.SqlDataReader
Dim cmdCommand As SqlClient.SqlCommand
Dim dtsData As New DataSet
Dim dtbTable As New DataTable
Dim i As Integer
Dim SQLStatement as String
msqlConnection.Open()
cmdCommand = New SqlClient.SqlCommand(SQLStatement, msqlConnection)
rdrDataReader = cmdCommand.ExecuteReader()
For i = 0 To (rdrDataReader.FieldCount - 1)
dtbTable.Columns.Add(rdrDataReader.GetName(i), rdrDataReader.GetFieldType(i))
Next
dtbTable.BeginLoadData()
Dim values(rdrDataReader.FieldCount - 1) As Object
While rdrDataReader.Read
rdrDataReader.GetValues(values)
dtbTable.LoadDataRow(values, True)
End While
dtbTable.EndLoadData()
dtsData.Tables.Add(dtbTable)
msqlConnection.Close()
Return dtsData