How to check if a record exists in an Access database - database

I'm trying a new approach for a project that I'm working on and I'm just starting to learn about Access Databases. I using VB.net and my question is: How do you see if a record exists in the table of the database. I thought I had it understood but that is not the case. I'm creating a login and I want it to check if the Username that they typed in exists before it tries to compare what you typed with what's in the database. I see alot of questions on how to do this...but not for VB.net and MS Access
Here's my code:
Imports System.Data.OleDb
Public Class LoginForm1
Dim provider As String
Dim dataFile As String
Dim connString As String
Public myConnection As OleDbConnection = New OleDbConnection
Public dr As OleDbDataReader
Dim Errors As String
Public Sub AccessAccountDatabase()
provider = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source ="
dataFile = "C:\Users\Richard\Documents\Visual Studio 2010\Projects\CybSol Journal Database\CybSol Journal Database\cgi-bin\Data.mdb"
connString = provider & dataFile
myConnection.ConnectionString = connString
Errors = ""
Try
myConnection.Open()
Dim str As String
str = "SELECT * FROM Accounts WHERE Username='" & UsernameTxt.Text & "' AND Password='" & PasswordTxt.Text & "'"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
dr = cmd.ExecuteReader
dr.Read()
If UsernameTxt.Text = dr("Username").ToString AndAlso PasswordTxt.Text = dr("Password").ToString Then
Dim Welcome As String = "SELECT * FROM Accounts WHERE Real_Name=" & "Username"
MsgBox("Welcome back " & dr("Real_Name") & "!")
Else
MsgBox("Login Failure")
End If
myConnection.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Private Sub OkayBtn_Click(sender As System.Object, e As System.EventArgs) Handles OkayBtn.Click
AccessAccountDatabase()
End Sub
End Class
So now my question is... How do you get it to check if a record exists in the database, because when you type in the correct information (The correct username and password that exists in the database) it says welcome and all. But when you type in the wrong Username and/or Password it doesn't work. Without the "Try Catch" statement the program just freezes. With the try catch it states this:
System.InvalidOperationException: No data exists for the row/column.
at System.Data.OleDb.OleDbDataReader.DoValueCheck(Int32 ordinal)
at System.Data.OleDb.OleDbDataReader.GetValue(Int32 ordinal)
at System.Data.OleDb.OleDbDataReader.get_Item(String name)
at CybSol_Journal_Database.LoginForm1.AccessAccountDatabase() in c:\users\richard\documents\visual studio 2010\Projects\CybSol Journal Database\CybSol Journal Database\LoginForm1.vb:line 36
Addition information: line 36 is this: If UsernameTxt.Text = dr("Username").ToString AndAlso PasswordTxt.Text = dr("Password").ToString Then

First problem:
PASSWORD is a reserved keyword in Access. You should encapsulate in square brackets:
"SELECT * FROM Accounts WHERE Username='" & UsernameTxt.Text & _
"' AND [Password]='" & PasswordTxt.Text & "'"
Second problem:
NEVER use string concatenation to create sql text. ALWAYS use parameters
str = "SELECT * FROM Accounts WHERE Username=? AND [Password]=?"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
cmd.Parameters.AddWithValue("user", UserNameTxt.Text)
cmd.Parameters.AddWithValue("pass", PasswordTxt.Text)
dr = cmd.ExecuteReader
Why? look here what could happen if you concatenate strings from user input
Third problem: Test if your command returns rows
If dr.Read() Then
......
End if

I added some Using statements so you don't have to manually close the connections. Also, I parameterized the SQL statement to prevent SQL Injection.
Public Class LoginForm1
Dim provider As String
Dim dataFile As String
Dim connString As String
'Public myConnection As OleDbConnection = New OleDbConnection
'Public dr As OleDbDataReader
Dim Errors As String
Public Sub AccessAccountDatabase()
provider = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source ="
dataFile = "C:\Users\Richard\Documents\Visual Studio 2010\Projects\CybSol Journal Database\CybSol Journal Database\cgi-bin\Data.mdb"
connString = provider & dataFile
myConnection.ConnectionString = connString
Errors = ""
Try
Using myConnection As OleDbConnection = New OleDbConnection(connString)
myConnection.Open()
Dim str As String
str = "SELECT * FROM Accounts WHERE Username=#USER AND [Password]=#PWD "
Using cmd As OleDbCommand = New OleDbCommand(str, myConnection)
cmd.Parameters.AddWithValue("#USER", UsernameTxt.Text)
cmd.Parameters.AddWithValue("#PWD", PasswordTxt.Text)
Using dr As OleDbDataReader = cmd.ExecuteReader
If dr.HasRows Then
dr.Read()
If UsernameTxt.Text = dr("Username").ToString AndAlso PasswordTxt.Text = dr("Password").ToString Then
Dim Welcome As String = "SELECT * FROM Accounts WHERE Real_Name=" & "Username"
MsgBox("Welcome back " & dr("Real_Name") & "!")
Else
MsgBox("Login Failure")
End If
Else
MsgBox("Login Failure")
End If
End Using
End Using
End Using
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Private Sub OkayBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OkayBtn.Click
AccessAccountDatabase()
End Sub
End Class

You're on the right track. The OleDbDataReader.Read returns a boolean indicating whether or not it successfully read an existing row. Therefore, you can check to see if it returned True before trying to read the record. For instance:
If dr.Read() Then
If UsernameTxt.Text = dr("Username").ToString AndAlso PasswordTxt.Text = dr("Password").ToString Then
Dim Welcome As String = "SELECT * FROM Accounts WHERE Real_Name=" & "Username"
MsgBox("Welcome back " & dr("Real_Name") & "!")
Else
MsgBox("Login Failure")
End If
End If
Also, I feel I should at least mention that storing a password in plain text is never a good idea.

You don't have to check for the username and password in your code again since if does not match in the database, no rows will be returned.
You can simply do
dr = cmd.ExecuteReader
If dr.HasRows Then
//it matched
Else
//it didn't match. could not log in
End If
Your approach is below if you still want to keep it but it's not necessary
dr = cmd.ExecuteReader
If dr.HasRows Then
dr.Read()
If UsernameTxt.Text = dr("Username").ToString AndAlso PasswordTxt.Text = dr("Password").ToString Then
Else
End If
End If

Use the Read() method on your DataReader (note that this keeps your connection to the database open and you'll be unable to execute any other commands on the database while your DataReader is still Reading.
If String.Compare(dr("Username").ToString(), UsernameTxt.Text, true) AndAlso String.Compare(dr("Password").ToString(), PasswordTxt.Text.ToString() Then
' The username and password for the record match
' the input from the login form
ProcessLogin()
Else
' Invalid username or password, send an error
End If

Related

How to resolve : 'There is already an open DataReader associated with this Command which must be closed first.'

I've been trying to insert data into my sql database but this problem always show up
i've tried redoing it again and the same problem occurs and i'm really stumped right now
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim conn As SqlConnection = New SqlConnection("Data Source=DESKTOP-OBQR58O\SQLEXPRESS;Initial Catalog=Accounts;Integrated Security=True")
Dim comm As SqlCommand = New SqlCommand("insert into User(username, password)values('" + TextBox1.Text + "', '" + TextBox3.Text + "')", conn)
Dim data As SqlDataAdapter = New SqlDataAdapter(comm)
Dim user = TextBox1.Text
Dim pass = TextBox2.Text
Dim cpass = TextBox3.Text
Dim reader As SqlDataReader
conn.Open()
Dim cmd As SqlCommand = New SqlCommand("select Username from [User] where Username ='" + TextBox1.Text + "'", conn)
conn.Close()
conn.Open()
reader = cmd.ExecuteReader
If user.Trim() = "" Or pass.Trim() = "" Or cpass.Trim() = "" Then
MessageBox.Show("Empty Fields", "Blank Spaces")
ElseIf Not String.Equals(pass, cpass) Then
MessageBox.Show("Passwords do not match", "ERROR")
conn.Close()
ElseIf reader.HasRows Then
MessageBox.Show("Username already exists!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
TextBox1.Clear()
conn.Close()
reader.Close()
Else
MessageBox.Show("Account created succesfully!", "Success")
Dim table As DataTable = New DataTable()
data.Fill(table) ' this is where the problem occurs.
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
Dim log As New Login
Me.Close()
log.Show()
conn.Close()
End If
conn.Close()
End Sub
I honestly don't know what to do
You open your reader up at the top:
reader = cmd.ExecuteReader
So, it's open. And then, when you run the Fill command, it conflicts with the open reader!
The simplest fix - although, personally, I would restructure the code a bit, to bring the OpenReader nearer to where it is used - would be to add a Close to your reader right before the Fill.
Else
reader.Close() ' what you would add
MessageBox.Show("Account created succesfully!", "Success")
Dim table As DataTable = New DataTable()
data.Fill(table) ' this is where the problem occurs.
VERY IMPORTANT: If you're not familiar with the concept of "SQL Injection Attacks", read up on them, right away. You should NEVER execute SQL that's been built by constructing a string with unvalidated data from the user. You should pass parameters instead.
After all, what if I typed in the user name of "Irrelevant';DROP TABLE Users;--"? You'd wind up with a SQL Statement that contained "SELECT Username from [Users] WHERE [Username] = 'Irrelevant'; DROP TABLE Users; --'"
And, of course, you should validate the input as well, for things like embedded HTML and script! But that's more complicated than just using SQL Parameters.

ExecuteScalar connection property has not not been initialized

When I run this piece of code I get the error:
ExecuteScalar connection property has not not been initialized
I can't seem to find why the connection isn't working.
Code:
Protected Sub btnTrackRepair_Click(sender As Object, e As EventArgs) Handles btnTrackRepair.Click
Using conn As New SqlConnection("Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\ITrepair.mdf;Integrated Security=True")
conn.Open()
Dim cmd As New SqlCommand(conn.ToString)
Dim txtTracking As String
cmd.CommandText = "SELECT Repair_Status FROM Repair WHERE Tracking_Number =" & txtTrack.Text
txtTracking = If(IsDBNull(cmd.ExecuteScalar), "", cmd.ExecuteScalar)
If txtTracking <> "" Then
MsgBox("Record Found!", MsgBoxStyle.Information, "Update")
txtStatus.Text = ""
txtStatus.Text = txtTracking
Else
MsgBox("No Record Found!", MsgBoxStyle.Information, "INFO.")
End If
End Using
End Sub
The code breaks at txtTracking = If(IsDBNull(cmd.ExecuteScalar), "", cmd.ExecuteScalar)
I have looked at other questions regarding this error however most are C# and I'm using VB.NET so I have found it difficult to find a solution.
You are using the SqlCommand constructor which takes only a string. But this is not the connection-string but the text of the query. So you've done it wrong.
Dim cmd As New SqlCommand("SELECT Repair_Status FROM Repair WHERE Tracking_Number = #Tracking_Number", conn)
Apart from that you should really get familiar with parameterized queries. Don't use string concatenation to build your queries to avoid (among other issues) sql-injection attacks.
Here is the complete method:
Protected Sub btnTrackRepair_Click(sender As Object, e As EventArgs)
Dim sqlQuery = "SELECT Repair_Status FROM Repair WHERE Tracking_Number = #Tracking_Number"
Using conn As New SqlConnection("Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\ITrepair.mdf;Integrated Security=True")
conn.Open()
Dim cmd As New SqlCommand(sqlQuery, conn)
cmd.Parameters.Add("#Tracking_Number", SqlDbType.NVarChar).Value = txtTrack.Text
Dim statusObj = cmd.ExecuteScalar()
Dim status = If(statusObj is DBNull.Value, Nothing, DirectCast(statusObj, string))
If not String.IsNullOrEmpty(status) Then
MsgBox("Record Found!", MsgBoxStyle.Information, "Update")
' ... '
Else
MsgBox("No Record Found!", MsgBoxStyle.Information, "INFO.")
End If
End Using
End Sub
If the Tracking_Number is not a varchar/nvarchar but an int(for example) in the database, you should parse it already here and use the correct SqlDbType.

VB.NET SQL Server Select Count(*) - ExecuteNonQuery: Connection property has not been initialized

I have my code VB.NET code for the login page like this:
Private Sub OK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK.Click
Try
If UsernameTextBox.Text = "" Then
MsgBox("Insert your username.")
UsernameTextBox.Focus()
Return
ElseIf PasswordTextBox.Text = "" Then
MsgBox("Insert your Passwprd.")
PasswordTextBox.Focus()
Return
Else
Dim count As Integer
Using con As New OleDbConnection("Provider=SQLOLEDB;Data Source=JUNIOR-PC\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=ShopHereNow")
con.Open()
Dim command = New OleDbCommand("Select count(*) from Employees where FirstName = '" & UsernameTextBox.Text & "' and [Password] = '" & PasswordTextBox.Text & "'")
count = command.ExecuteNonQuery()
con.Close()
If count > 0 Then
MsgBox("Welcome " & UsernameLabel.Text & "!")
Me.Hide()
Home.Show()
Else
MessageBox.Show("Invalid combination. Try again...")
Cancel.PerformClick()
Return
End If
End Using
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "ERROR5", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
On executing this code i get the error reading: ExecuteNonQuery: Connection property has not been initialized
Please help, where could I have coded a mistake?
Your code have some mistakes,
Use Parameterized Queries :
Dim command = New OleDbCommand("Select count(*) from Employees where FirstName =#FirstName and Password = #Password")
command.Parameters.AddWithValue("#FirstName", UsernameTextBox.Text)
command.Parameters.AddWithValue("#Password", PasswordTextBox.Text)
This is to avoid SQL Injection Attack AKA For Security
The error mentioned above is because you haven't specify the connection in your command, change your Dim command = New OleDbCommand("Select count(*) from Employees where FirstName =#FirstName and Password = #Password") to Dim command = New OleDbCommand("Select count(*) from Employees where FirstName =#FirstName and Password = #Password", **con**)
MsgBox() is a deprecated method, use MessegeBox.Show() instead.
Do not use Class Exception. Instead specify the specific Exception type. In here, you may need SQLException

When selecting data from a database (SELECT * FROM) only last row is being read

I am making a create an account system, and in this I am using a SELECT * FROM query to read all of the data in the database to see if the inputted username already exists however this is only returning the very last user in the database, so if the inputted user matches any other user excluding the last saved account, it will not recognize that the user already exists therefore creating an account with the same username. Below is my code for the sub routine. can anyone please help me find what I have done wrong
Dim conn As New OleDbConnection
Dim myqry As String = Nothing
Dim mycmd As New OleDbCommand
Dim mydr As OleDbDataReader
Private Sub btn_createAccount_Click(sender As System.Object, e As System.EventArgs) Handles btn_createAccount.Click
'Connecting to the database
Try
With conn
If .State = ConnectionState.Open Then .Close()
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Database.accdb"
.Open()
End With
Catch ex As Exception
MessageBox.Show("Unable to connect", "error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Dim student_Name As String
Dim student_Username As String
Dim student_Password As String
Dim student_Group As String
student_Name = txt_firstname.Text & " " & txt_surname.Text
student_Username = LCase(txt_Username.Text)
student_Password = txt_password.Text
student_Group = cbo_tutorGroup.SelectedItem
'This chunk of code is reading the username column in the student account table in my database and doing a read to see if the inputted username is already existent in the table.
myqry = "SELECT * FROM TblStudents"
mycmd = New OleDbCommand(myqry, conn)
mydr = mycmd.ExecuteReader
While mydr.Read
Dim user As String = mydr("studentUser").ToString
If user = student_Username Then
MsgBox("Username already exists, please choose another")
Else
'If the username is not taken, the account credentials will be stored for use when logging in
Dim sqlQry As String
sqlQry = "INSERT INTO TblStudents(studentName, tutorGroup, studentUser, studentPass) VALUES('" & student_Name & "','" & student_Group & "','" & student_Username & "','" & student_Password & "')"
Dim cmd As New OleDbCommand(sqlQry, conn)
cmd.ExecuteNonQuery()
MsgBox("Your account has successfully been created")
Login_Student.Show()
Me.Close()
Exit Sub
End If
End While
I think you are misinterpreting your results. The query may return many rows but if the first one does not match your condition (user = student_Username), then you insert a record and exit the procedure.
Why don't you check if your specific user exists:
SELECT * FROM TblStudents WHERE studentUser = :student_Username
instead of checking each student?

"There is no row at position 0"?

Private Sub BtnLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnLogin.Click
Dim sql As String
sql = " SELECT * FROM LoginDetails WHERE UsernameID = '" & TxtUsername.Text & "' AND Password = '" & TxtPassword.Text & "'"
ds = db.sqlSelect(sql)
Dim i As Integer
Dim Username As String = ds.Tables("LoginDetails").Rows(i)("UsernameID")
Dim Password As String = ds.Tables("LoginDetails").Rows(i)("Password")
''''''STUDENT LOGIN'''''''
If TxtUsername.Text = "" And TxtPassword.Text = "" Then
MsgBox("No username and password entered!")
ElseIf TxtUsername.Text = "" Then
MsgBox("No username entered!")
ElseIf TxtPassword.Text = "" Then
MsgBox("No password entered!")
End If
Username = TxtUsername.Text.ToLower
Password = TxtPassword.Text.ToLower
If TxtUsername.Text.ToLower = Username And TxtPassword.Text = Password Then
FrmMainMenu.Show()
Me.Hide()
FrmMainMenu.LblWelcome.Text = "Welcome " & ds.Tables("LoginDetails").Rows(i)("Student Name") & "!"
ElseIf TxtUsername.Text.ToLower = Username And TxtPassword.Text <> Password Then
MsgBox("Wrong password entered!")
End If
If TxtUsername.Text.ToLower <> Username And TxtPassword.Text <> Password Then
MsgBox("Wrong password or username!")
Else
End If
If Len(Username) <> 7 Then
MsgBox("Username must be exactly 7 characters long and must be in the following format: 1XlXXXX")
End If
If Len(Password) < 6 And Len(Password) > 30 Then
MsgBox("Password must be between 6 and 30 characters!")
End If
When I type in the correct details for the form it works, but whenever I type in say a wrong password, it crashes!
Can anyone help me solve this?? I keep getting this error!!!
IndexOutofRangeException was unhandled
There is no row at position 0.
Pointing at the code: Dim Username As String = ds.Tables("LoginDetails").Rows(i)("UsernameID")
Here is the class that links the code to my database:
Imports System.Data.OleDb
Public Class clsDBConnector
Dim con As New OleDbConnection
Dim dbProvider As String
Dim dbSource As String
Dim da As OleDbDataAdapter
Dim ds As New DataSet
Sub connect()
dbProvider = "PROVIDER=MICROSOFT.ACE.OLEDB.12.0;"
dbSource = "Data Source = E:\Computing\COMP4\Database.accdb "
con.ConnectionString = dbProvider & dbSource
con.Open()
End Sub
Function sqlSelect(ByVal sqlString As String)
da = New OleDbDataAdapter(sqlString, con)
da.Fill(ds, "LoginDetails")
Return ds
End Function
Sub reset()
ds.Reset()
End Sub
Sub SQLinsert(ByVal sql) 'inserts data into database
Dim da As New OleDbCommand(sql, Con)
da.ExecuteNonQuery()
End Sub
Function SQLupdate(ByVal sqlString As String)
da = New OleDbDataAdapter(sqlString, con)
da.Fill(ds, "LoginDetails")
Return ds
End Function
End Class
The first thing you're doing wrong is storing plain-text passwords. Never store plain-text passwords.
The second thing is that you're wide open to SQL injection attack. Use parameterized queries. Otherwise you're allowing users to arbitrarily execute any code they'd like on your database.
The third thing is that you're assuming a returned value here:
sql = " SELECT * FROM LoginDetails WHERE UsernameID = '" & TxtUsername.Text & "' AND Password = '" & TxtPassword.Text & "'"
ds = db.sqlSelect(sql)
Dim i As Integer
Dim Username As String = ds.Tables("LoginDetails").Rows(i)("UsernameID")
Dim Password As String = ds.Tables("LoginDetails").Rows(i)("Password")
If that SELECT statement doesn't find any values, then Rows(i) (i is 0 in this case since that's the default for an Integer) doesn't exist. You need to check the count of Rows before trying to access it. In this case, logically, if Rows.Count is 0 then no match was found for the username/password combination, so the login fails. Notify the user that the login has failed and stop executing anything else.
The fourth thing you're doing wrong is storing plain-text passwords. Never store plain-text passwords.
Before you start using the dataset after the SQL call you should alway check to see if you received valid/any data back . Call a DataSet check method like the one I included. If it returns false you know your SQL returned an empty DataSet. So you can display a message stating invalid login info..
bool IsEmpty(DataSet dataSet)
{
foreach(DataTable table in dataSet.Tables)
{ if (table.Rows.Count != 0) return false; }
return true;
}
Every time you try to take information from the DB, with something like:
If ds.tables("MyTable").rowsCount > 0 then
// Do the stuff
Else
// There is no information on the table
End If

Resources