ASP.NET - VB.NET - Updating MS_Access table - asp.net

I'm trying to update a record from an Ms-Access table with VB.NET and ASP.NET. I'm getting 2 errors:
On the web page that's opened I'm getting Thread was being aborted
Web Developer 2010 gives me an error says there's an error in the
UPDATE statement
This is the code so far:
Imports System.Data.OleDb
Partial Class ChangePassword
Inherits System.Web.UI.Page
Protected Sub btnChange_Click(sender As Object, e As System.EventArgs) Handles btnChange.Click
Dim tUserID As String = Session("UserID")
Dim conn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Brian\Documents\Visual Studio 2010\WebSites\WebSite3\db.mdb;")
conn.Open()
Dim cmd As OleDbCommand = New OleDbCommand("SELECT * FROM [User] where UserID=?", conn)
Dim cmd2 = New OleDbCommand("UPDATE USER SET [Password] = '" + txtConfPass.Text + "' where UserID = '" + tUserID + "'", conn)
cmd.Parameters.AddWithValue("#UserID", tUserID)
Dim read As OleDbDataReader = cmd.ExecuteReader()
Dim read2 As OleDbDataReader = cmd2.ExecuteReader()
lblUser.Text = tUserID.ToString
lblUser.Visible = True
If read.HasRows Then
While read.Read()
If txtOldPass.Text = read.Item("Password").ToString Then
cmd2.ExecuteNonQuery()
lblPass.Visible = True
End If
End While
Else
lblPass.Text = "Invalid Password."
lblPass.Visible = True
End If
conn.Close()
lblPass.Text = tUserID.ToString
lblPass.Visible = True
Any help would be appreciated.
Thanks !

First, your cmd2 fails because USER is a reserved word. Enclose in
square brackets as you already do in the first OleDbCommand.
Second, to execute a statement like UPDATE, INSERT, DELETE you call
cmd2.ExecuteNonQuery not ExecuteReader. Don't really needed that call
after the first for cmd.
Third, in the first OleDbCommand (cmd) you use a parameter for
UserID, why in the second one you revert to string concatenation for
user and password? This opens the door to any kind of Sql Injection
Attack.
Fourth, the Using statement assure that every Disposable object
used in your code will be CLOSED thus freeing the memory used by
this commands ALSO IN CASE OF EXCEPTIONS. An example of Using
statement here

(1)
Dim read2 As OleDbDataReader = cmd2.ExecuteReader()
and then
(2)
cmd2.ExecuteNonQuery()
Remove (1) - ExecuteNonQuery should do the update.
USER is a keyword in Access, add brackets the same way you have added in the Select statement. Next time, you are faced with a similar problem, print out the statement as Access would see it and try executing it on the database directly - that will point out the errors accurately.
Please use place holders for the update statement similar to the select statement.

Related

Execute Scalar to Label. Subquery returned more than 1 value

So I have a label which shows the username of the user. I've used this value to return their ID which I then attach to a label. I used execute scalar to do this because I wasn't sure how else to get a single value on a label.
This works fine. I then use the ID from the label and put it in another table. I can do this twice and then the page crashes saying...
"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
However I don't understand. I don't pull anything from the second table on the page. I don't know why it would affect it. I feel like I've tried everything. Taking out the line that posts the ID to the label lets the page run but I need it there.
Label2.Text = User.Identity.Name
Dim connetionString As String
Dim cnn As SqlConnection
Dim cmd As SqlCommand
Dim sql As String
connetionString = "Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\FYPMS_DB.mdf;Integrated Security=True"
sql = "SELECT SupID FROM Supervisor WHERE (Email = #Email)"
cnn = New SqlConnection(connetionString)
Try
cnn.Open()
cmd = New SqlCommand(sql, cnn)
cmd.Parameters.Add(New SqlParameter("#Email", User.Identity.Name))
Dim supid1 As Int32 = Convert.ToInt32(cmd.ExecuteScalar())
cmd.Dispose()
cnn.Close()
Label1.Text = supid1.ToString
Catch ex As Exception
MsgBox("Can not open connection ! ")
End Try
End Sub
This should return the first result for you. Also, it's a good idea to employ Using blocks for objects such as connections, commands, and readers.
Using cn = New SqlConnection("Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\FYPMS_DB.mdf;Integrated Security=True")
cn.Open()
Using cmd = New SqlCommand("SELECT SupID FROM Supervisor WHERE Email = #Email", cn)
cmd.Parameters.AddWithValue("#Email", User.Identity.Name)
Using dr = cmd.ExecuteReader
If dr.Read Then
Label1.Text = CInt(dr("SupID"))
End If
End Using
End Using
End Using
If you are not sure there are multiple rows for same email in that table, you can change the query to following, that will work for you with executescalar.
SELECT TOP 1 SupID FROM Supervisor WHERE (Email = #Email)
Horribly sorry! But yes you were right! There was another query going on in the background that I never noticed that was affecting it all. So sorry

ASP.NET VB.NET -- SQL UPDATE Command Not Working

I have been working on this particular issue for a couple of days, and scouring over SO, MSDN and other google searches has not proven to be of any use. I am trying to make a simple update to a SQL table. My SELECT and INSERT statements all work fine, but for some reason, this update will not work. I have set breakpoints and stepped through, and the code seems to be working fine -- the Catch ex as Exception is never reached after the .ExecuteNonQuery() fires off.
Could anyone give me an idea of why I've been unable to get a SQL update?
Protected Sub SaveButton_Click(sender As Object, e As EventArgs) Handles SaveButton.Click
Dim currentUser = Membership.GetUser(User.Identity.Name)
Dim username As String = currentUser.UserName
Dim userId As Guid = currentUser.ProviderUserKey
UserNameTextBox.Text = username
' Get Root Web Config Connection String so you don't have to encrypt it
Dim rootWebConfig As System.Configuration.Configuration
rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/")
Dim connString As System.Configuration.ConnectionStringSettings
connString = rootWebConfig.ConnectionStrings.ConnectionStrings("LocalSqlServer")
Dim conn As String = connString.ToString
Dim commandString As String = "UPDATE UserDetails SET FirstName ='" + FirstNameTextBox.Text + "' WHERE UserId ='" + userId.ToString + "'"
Dim fname As String = FirstNameTextBox.Text
Dim commandText As String = "UPDATE UserDetails SET FirstName=#firstname WHERE UserId=#UID;"
Using connection As New SqlConnection(conn)
Dim command As New SqlCommand(commandText, connection)
command.CommandType = CommandType.Text
' Add UserId parameter for WHERE clause.
command.Parameters.Add("#UID", SqlDbType.UniqueIdentifier).Value = userId
' command.Parameters("#UID").Value = userId
' command.Parameters.AddWithValue("#UID", userId)
' Use AddWithValue to assign Demographics.
command.Parameters.Add("#firstname", SqlDbType.VarChar, 255).Value = fname
'command.Parameters.AddWithValue("#firstname", fname)
' command.Parameters("#firstname").Value = FirstNameTextBox.Text.ToString
Try
connection.Open()
command.ExecuteNonQuery()
Dim rowsAffected As Integer = command.ExecuteNonQuery()
Console.WriteLine("RowsAffected: {0}", rowsAffected)
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
connection.Close()
End Try
End Using
End Sub
You're running "command.ExecuteNonQuery()" twice, meaning the second execution will likely return 0 rows affected since you already updated what you needed to update, and that's what you're assigning to rowsAffected. Are you sure the UPDATE isn't occurring?
Edit: Re your comment, did you check for IsPostBack when you LoadUser? If not, when you click SaveButton, you're going to reload the existing values, and then you'll be updating with those existing values.

Error: "No value given for one or more required parameters" on UPDATE query from ASP.NET

I receive the error
No value given for one or more required parameters
when I try to execute the following code
Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim t2 = TextBox2.Text
Dim dbcmd As OleDbCommand
dbcmd = New OleDbCommand("UPDATE login1 SET height ='" + TextBox2.Text + "' WHERE username =" + str, dbcon) //This is the part where the error is ....
dbcon.Open()
dbcmd.ExecuteNonQuery()
dbcon.Close()
End Sub
End Class
I've tried making changes in this statement like using different methods with parameters...but all the code shows the same error.
Using a parameterized query is definitely the way to go, but using named parameters should be discouraged in this context because OleDbCommand objects ignore the parameter names when the CommandType is Text. They only rely on the order in which the parameters appear in the CommandText (ref: here).
Therefore, the preferred approach would be
Using dbcmd As New OleDbCommand(
"UPDATE login1 SET height=? WHERE username=?",
dbcon)
dbcmd.Parameters.AddWithValue("?", TextBox2.Text) ' height
dbcmd.Parameters.AddWithValue("?", str) ' username
dbcmd.ExecuteNonQuery()
End Using
You don't supply the user name properly at the end of the query. But that's not the only problem here. Let me edit the code a bit:
Using dbcon As New OleDbConnection(cString)
dbcon.Open()
Using dbcmd As New OleDbCommand(
"UPDATE login1 SET height = #height WHERE username = #username",
dbcon)
dbcmd.Parameters.AddWithValue("#height", TextBox2.Text)
dbcmd.Parameters.AddWithValue("#username", str)
dbcmd.ExecuteNonQuery()
End Using
End Using
NOTE: the using statement to ensure the objects are disposed property and also, do not share connections. When you need a connection, build it, open it, use it, and dispose it.

Reducing SQL connections to just 1 - ASP.net VB

I am currently working on an asp.net web page with a GridView displaying a table from a database. This GridView has 4 DropDownLists that will be used to filter the data shown on the GridView. When the page loads 4 Sub routines are run, each one connecting to the database with a select statement to fill the DropDownList with relevant filter headings.
Initially, I had one connection with a loop that populated all of the drop downs but these contained duplicates. I then split the filling of each DDL so that the select statements could contain DISTINCT.
I would like (and am sure there is a way here) to be able to populate all of the DDLs with data from one connection.
Code for one connection:
Protected Sub FillDepDDL()
Dim conn As New SqlConnection()
conn.ConnectionString = WebConfigurationManager.ConnectionStrings("TestDBConnectionString").ConnectionString
Dim connection As New SqlConnection(conn.ConnectionString)
connection.Open()
Const FillAllQS As String = "SELECT DISTINCT [Department] FROM [Employees]"
Dim command As New SqlCommand(FillAllQS, connection)
Dim reader As SqlDataReader = command.ExecuteReader()
Dim sel As New ListItem
sel.Text = "Please Select"
sel.Value = "*"
DDLDepartment.Items.Add(sel)
While reader.Read
Dim Deplist As New ListItem()
Deplist.Value = reader("Department")
Deplist.Text = reader("Department")
DDLDepartment.Items.Add(Deplist)
End While
reader.Close()
conn.Close()
End Sub
The other 3 column names: FirstName > DDLFN, LastName > DDLLN, Wage > DDLWag.
This is only a test DB and the princibles learned here will be applied to a larger live project.
I'm sure some guru will be able to work this out easily but I just can't get my head round it even after hours of searching.
Thanks in advance.
I'm adding this in as answer because I cannot format it in a comment, but this doesn't answer the original question of how to write the sql to return all three distinct result sets. Instead, it answers how to rewrite the code you have above so that connections are properly disposed of in case of an exception.
Protected Sub FillDepDDL()
Dim Deplist As ListItem
Dim sel As New ListItem
sel.Text = "Please Select"
sel.Value = "*"
DDLDepartment.Items.Add(sel)
Using conn As New SqlConnection(WebConfigurationManager.ConnecitonString("TestDBConnectionString").ConnectionString)
Using cmd As New SqlCommand("SELECT DISTINCT [Department] FROM [Employees]", conn)
conn.Open()
Using reader = cmd.ExecuteReader()
While reader.Read
Deplist = New ListItem()
Deplist.Value = reader("Department")
Deplist.Text = reader("Department")
DDLDepartment.Items.Add(Deplist)
End While
End Using
End Using
End Using
End Sub
I don't see any reason for you to try to return all three results in a single query. That will just make your code unnecessarily complicated just to save a millisecond or two. Connection pooling handles the creation of connections on the database server for you, so opening a new connection in your code is very fast.

ASP.NET SqlConnection error: "The ConnectionString property has not been initialized"

What's wrong this T-SQL query :
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim SQLData As New System.Data.SqlClient.SqlConnection("Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database.mdf;Integrated Security=True;User Instance=True")
Dim cmdSelect As New System.Data.SqlClient.SqlCommand("SELECT COUNT(*) FROM Table1 WHERE Name ='" + TextBox1.Text + "'", SQLData)
SQLData.Open()
If cmdSelect.ExecuteScalar > 0 Then
Label1.Text = "You have already voted this service"
Return
End If
Dim con As New SqlConnection
Dim cmd As New SqlCommand
con.Open()
cmd.Connection = con
cmd.CommandText = "INSERT INTO Tabel1 (Name) VALUES('" & Trim(Label1.Text) & "')"
cmd.ExecuteNonQuery()
Label1.Text = "Thank You !"
SQLData.Close()
End Sub
Your problem is that you are opening a connection (SQLData), ignoring it, then trying to open a new connection (con) without giving it a connection string. Instead of this:
Dim con As New SqlConnection
Dim cmd As New SqlCommand
con.Open()
cmd.Connection = con
you should have:
Dim cmd As New SqlCommand
cmd.Connection = SQLData
Also, it is very bad practice to insert string value inline in SQL as you have.
I would recommend an approach something like this:
Protected Function Button1_Click(sender As Object, e As System.EventArgs)
' define and create your one single SqlConnection and protect it by using a "using()....." block
Using _connection As New SqlConnection("Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database.mdf;Integrated Security=True;User Instance=True")
' define and craete your SqlCommand to count your occurences and make it a proper, parametrized query
Using cmdSelect As New SqlCommand("SELECT COUNT(*) FROM dbo.Table1 WHERE Name = #Name", _connection)
' add the parameter to your SqlCommand, define the datatype and length
cmdSelect.Parameters.Add("#Name", SqlDbType.VarChar, 100)
' set the value for that parameter
cmdSelect.Parameters("#Name").Value = TextBox1.Text.Trim()
' open connection, execute query, set return value
_connection.Open()
If cmdSelect.ExecuteScalar() > 0 Then
Label1.Text = "You have already voted this service"
Return
End If
End Using
' define second query to insert data reusing the existing connection
Using cmdInsert As New SqlCommand("INSERT INTO dbo.Table1(Name) VALUES(#Name)", _connection)
' add the parameter to your SqlCommand, define the datatype and length
cmdInsert.Parameters.Add("#Name", SqlDbType.VarChar, 100)
' set the value for that parameter
cmdInsert.Parameters("#Name").Value = Label1.Text.Trim()
cmdInsert.ExecuteNonQuery()
End Using
_connection.Close()
End Using
Label1.Text = "Thank You !"
End Function
Points to consider:
you have one SqlConnection - that's good enough for both queries, reuse it!
always put your disposable objects like SqlConnection, SqlCommand into Using..... blocks to protect them and make sure they get properly disposed
always use parametrized queries - do NOT under any circumstances just concatenate together your SQL statements - that's a big huge gaping security hole, inviting SQL injection attacks - just DON'T do it - EVER!
if I could, I would try to separate your UI elements from the code - try to put this code into a separate method that will take in the string values from the caller, and will return a result string to be set on the UI (Label1.Text=). Mixing code that queries the database and setting the UI at the same time is messy and leads to spaghetti code - try to separate those things
put your connection string into the web.config into the <connectionStrings> section and read it from there - don't have your connection string as a string literal all throughout your code!
There's a few things I see wrong there. First, (other than the SQL injection vulnerability) is that you typed Table1 once, and Tabel1 the other time. While that could be what you want, I doubt it. Next you're creating a second connection. That doesn't seem to be needed. Use the existing SQLData object instead of con. You can also reduce the lines starting from the declaration of cmd (inclusive) to the ExecuteNonQuery call (exclusive) with this:
Dim cmd As New SqlCommand("INSERT INTO Tabel1 (Name) VALUES('" & Trim(Label1.Text) & "')", SQLData)
Now back to that SQL injection vulnerability. What if someone's name is "James O'Brian" (or something else with an apostrophe in it)?

Resources