Retrieving ##IDENTITY in AccessDB - vb.net

I'm trying to retrieve the ##IDENTITY value from an access database after I add a row to it. However, instead of using hard coded connections, I'm using the DataSet wizard. Unfortunately, due to the Jet engine, I can't do more than one command at a time. I tried to select the ##IDENTITY in a separate command but unfortunately, I guess it counts as a different command as it returns a 0 each time.
My question is, is there a way I can use the GUI/IDE to retrieve the ##IDENTITY or do I have hard code the connection, command, query values and obtain the value that way.
Thanks.

You asked an interesting questions which I did not know the answer to. After some research I found this and it seems promising. However I have never used it and cannot substantiate if and how well it works.
I also don't know the DataSet wizard that well, but vaguely recalls that it generates an OleDbAdapter object, hopefully it exposes a RowUpdated event which you can hook this code to.
I have pasted the interesting MSDN code parts here:
(Link to full documentation)
' Create the INSERT command for the new category.
adapter.InsertCommand = New OleDbCommand( _
"INSERT INTO Categories (CategoryName) Values(?)", connection)
adapter.InsertCommand.CommandType = CommandType.Text
Then hook-up and event listener to RowUpdated
' Include an event to fill in the Autonumber value.
AddHandler adapter.RowUpdated, _
New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
Obtain the ##Identity with the same connection.
Private Shared Sub OnRowUpdated( _
ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs)
' Conditionally execute this code block on inserts only.
If e.StatementType = StatementType.Insert Then
' Retrieve the Autonumber and store it in the CategoryID column.
Dim cmdNewID As New OleDbCommand("SELECT ##IDENTITY", _
connection)
e.Row("CategoryID") = CInt(cmdNewID.ExecuteScalar)
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
Hopefully this helps you out or least someone will set me straight :)

Related

How to edit a record in an access database - visual basic

I want to edit a specific record in an access database but I keep on getting errors
this is the database I want to edit:
Access database
these are flashcards that the user has created and stored in an access database. What I want is that the user is able to edit the difficulty so it appears more/less often
This is the module:
Module Module1
Public Function runSQL(ByVal query As String) As DataTable
Dim connection As New OleDb.OleDbConnection("provider=microsoft.ACE.OLEDB.12.0;Data Source=flashcard login.accdb") 'Establishes connection to database
Dim dt As New DataTable 'Stores database in table called dt
Dim dataadapter As OleDb.OleDbDataAdapter
connection.Open() 'Opens connection
dataadapter = New OleDb.OleDbDataAdapter(query, connection)
dt.Clear() 'Clears datatable
dataadapter.Fill(dt) 'Fills datatable
connection.Close()
Return dt
End Function
End Module
And here is the button that the user can press to edit the database:
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = '" & TxtDifficulty.Text
runSQL(sql)
End Sub
The difficulty column in the database should be able to be edited by the user through the value they entered in txtDifficulty.text
Good to hear I found the problem with the apostrophe.
I am going to need a where statement but the problem I have is that the user can create as much flashcards as they want so how would I write the where statement?
An INSERT statement does not have a WHERE clause but an UPDATE does and is usually by a primary key.
Look at how I add a new record ignoring mHasException and specifically using parameters. In this case a List is used but with little effort an array of DataRow can be passed instead.
Here is an example for updating a record with a DataRow.
To get other code samples for ms-access see the following repository.
In closing, in the above repository I don't get into all possibilities yet there should be enough there to get you going. And when reviewing code I flip between Add for adding a parameter and AddWithValue while Add is the recommend way but both are shown to see differences. see also Add vs AddWithValue.

How to verify login permissions using a DataSet and QueryBuilder?

I wasnt quite sure how to properly write the question so bear with me please, here i'll explain as much as I can. And sorry if its too long.
I am building a basic school project that allows employees to register their time-in and time-out of the job to an Access Database, but I am currently struggling with the Login Form.
After many attempts I decided to go the easy way and use both the DataSet designer and the Query Builder from Visual Studio.
This is what I have right now:
An "EmpleadosDataSet" with a "_tblUser" DataTable and its TableAdapter, this adapter uses a query which I've named "GetDataLogin". Heres an image for reference EmpleadosDataSet Design View
What the query does is fill the "_tblUserDataTable" like this:
SELECT [ID EMPLEADO], USUARIO, CONTRASEÑA, PERMISOS
FROM [_tblUser]
WHERE (USUARIO = ?) AND (CONTRASEÑA = ?)
(Notice how I'm not using the PERMISOS at all after selecting it because that´s what I need help with)
For this to work on my code I instantiate my _tblUserDataTable from the EmpleadosDataSet and its TableAdapter:
Private dtLogin As New EmpleadosDataSet._tblUserDataTable
Private taLogin As New EmpleadosDataSetTableAdapters._tblUserTableAdapter
Then I pass both onto a button click event with this:
Private Sub _Button2_Acceder_Click(sender As Object, e As EventArgs) Handles _Button2_Acceder.Click
dtLogin = taLogin.GetDataLogin(_TxtUsuario.Text, _TxtContrasena.Text)
If dtLogin.Count > 0 Then
MsgBox("Bienvenido")
Else
MsgBox("No esta registrado")
End If
End Sub
This works just fine, however, I want to open a different form depending on whether the employee is an "user" or an "admin". This information is stored in the same dataset on a column named "PERMISOS", but I have no clue how to make the query that selects the value from said column and verify it.
My best bet is that I´ll have the query do SELECT PERMISOS and somehow assign that value to a variable that I can then verify with an If statement?
Something with this structure (this is just an example of what I need but I do not know if its the correct procedure, also, value from PERMISOS column is not a variable name or anything, im putting it for reference of what i need to be there):
If **value from PERMISOS column** = "admin" Then
Me.Hide()
Form2_admin.Show()
ElseIf **value from PERMISOS column** = "user" Then
Me.Hide()
Form3_user.Show()
I am not sure if that would be correct at all, bear with me, I havent been able to find how to even do that, assign the value of the column that I already selected with the query to a variable. Every info about this that I find is from people who rather code the whole database connection and queries themselves, haven´t found anything using the designer and query builder that refers to user permissions.
So that's basically what I need help with, how to either use a query or something else to verify user permissions like that, in other words, how to get the -value from PERMISOS column-
And just if clarification is needed, I did also use the Data Source wizard to connect to the Access Database, as it was the easier way, my DataSets are connected to that and filled with the info from it
PS: I understand some of my methods are incorrect, such as storing passwords as plain text, but please understand this is a simple practice about WindowsForms in general, not trying to learn security or others as of right now, will eventually do but right now I just need this.
If you can learn to write some basic ADO.net code, you will be able to get the data you want in the format your want.
First the user interface code. You pass the values in the 2 text boxes to a function and get back the data you want. Notice that the user interface doesn't know or care where the data is coming from. It is completely disconnected from the database.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
Dim permission = ValidateEmployee(_TxtUsuario.Text, _TxtContrasena.Text)
If permission = "admin" Then
Me.Hide()
Form2_admin.Show()
ElseIf permission = "user" Then
Me.Hide()
Form3_user.Show()
Else
MessageBox.Show("No esta registrado")
End If
End Sub
The data access code is all in the function and this function doesn't know anything about the user interface. It has not idea that the parameters are coming from text boxes. If you switched this to a web app, you could still use this function; just change where the parameters values are coming from.
The Using blocks ensure that your database objects are closed and disposed even if there is an error.
We can use named parameters with Access but Access pays no attention to the name. For Access (OleDb) these are positional parameters which means that the order that they appear in the sql query must match the order that they are added to the parameters collection (cmd.Parameters.Add)
Private ConStr As String = "Your connection string"
Private Function ValidateEmployee(user As String, pword As String) As String
Dim permissions As String
Using cn As New OleDbConnection(ConStr),
cmd As New OleDbCommand("Select PERMISOS
From _tblUser
Where USUARIO = #User
AND CONTRASEÑA = #Password;", cn)
cmd.Parameters.Add("#User", OleDbType.VarWChar).Value = user
cmd.Parameters.Add("#Password", OleDbType.VarWChar).Value = pword
cn.Open()
Using reader = cmd.ExecuteReader
If reader.HasRows Then
reader.Read()
permissions = reader("PERMISOS").ToString
Else
permissions = ""
End If
End Using
End Using
Return permissions
End Function
The only big problem left is storing passwords as plain text. They should be salted and encrypted to be safe but that is an issue for another day.

Simultaneous OleDbDataAdapter.Fill Calls on Separate Threads?

First timer here, so go easy on me. Is it theoretically possible to execute two OleDBDataAdapter.Fill calls on separate threads simultaneously - or is that fundamentally flawed?
Consider a form with 2 buttons and 2 datagridviews. Each button click launches a worker thread using an Async \ Await \ Task.Run pattern that calls a method to return a populated datatable and assigns it to one of the datagridviews. The .Fill in the first thread takes 30 seconds to complete. The .Fill in the second thread takes 1 second to complete. When launched individually, both buttons work as expected.
However, if I launch the first worker thread (30 seconds to Fill), then launch the second thread (1 second Fill), the second DataGridView is not populated until the first .Fill call completes. I would expect the second datagridview to populate in 1 second, and the first datagridview to populate ~30 seconds later.
I have duplicated this issue in my sample code with both the OleDBDataAdapter and the SqlDataAdapter. If I replace the long running query with a simple Thread.Sleep(30000), the second datagridview is populated right away. This leads me to be believe that it is not an issue with my design pattern, rather something specific to issuing the .Fill calls simultaneously.
Private Async Sub UltraButton1_Click(sender As Object, e As EventArgs) Handles UltraButton1.Click
Dim Args As New GetDataArguments
Args.ConnectionString = "some connection string"
Args.Query = "SELECT LongRunningQuery from Table"
Dim DT As DataTable = Await Task.Run(Function() FillDataTable(Args))
If DataGridView1.DataSource Is Nothing Then
DataGridView1.DataSource = DT
Else
CType(DataGridView1.DataSource, DataTable).Merge(DT)
End If
End Sub
Function FillDataTable(Args As GetDataArguments) As DataTable
Dim DS As New DataTable
Using Connection As New OleDbConnection(Args.ConnectionString)
Using DBCommand As New OleDbCommand(Args.Query, Connection)
Using DataAdapter As New OleDbDataAdapter(DBCommand)
DataAdapter.Fill(DS)
End Using
End Using
End Using
Return DS
End Function
Private Async Sub UltraButton2_Click(sender As Object, e As EventArgs) Handles UltraButton2.Click
Dim DS As DataTable = Await Task.Run(Function() LoadSecondDGV("1234"))
DataGridView2.DataSource = DS
End Sub
Function LoadSecondDGV(pnum As String) As DataTable
Dim DX As New DataTable
Using xConn As New OleDbConnection("some connection string")
Using DataAdapter As New OleDbDataAdapter("Select name from products where PNUM = """ & pnum & """", xConn)
DataAdapter.Fill(DX)
End Using
End Using
Return DX
End Function
This depends on what the data source is. Some data sources (like Excel) will only allow one connection at a time. Other data sources (like Access) will allow multiple connections, but actually fulfill the results in serial, such that you don't gain anything. Other data sources, like Sql Server, will allow the true parallel activity that you're looking for.
In this case, you mention that you also tried with an SqlDataAdapter, which indicates to me that you're talking to Sql Server, and this should be possible. What's probably going on here is that your first query is locking some of the data you need for the second query. You can get past this by changing your transaction isolation level or through careful use of the with (nolock) hint (the former option is strongly preferred).
One other thing to keep in mind is that this can only work if you're using a separate connection for each query, or if you've specifically enabled the Multiple Active Result Sets feature. It looks like you're using separate connection objects here, so you should be fine, but it's still something I thought was worth bringing up.
Finally, I need to comment on your FillDataTable() method. This method requires you to provide a completed Sql string, which practically forces you to write code that will be horribly vulnerable to sql injection attacks. Continuing to use the method as shown practically guarantees your app will get hacked, probably sooner rather than later. You need to modify this method so that it encourages you to use parameterized queries.

VB.Net and SQL Communication

I haven't done any serious programming in years, and I don't have much experience manipulating SQL data indirectly anyway, but I'm trying to create a program for my employer and running into confusion. Many of the answers here and college books i have are helpful, but just when I think I understand what's going on, I get lost again, often because people use variables and column names that are identical, leaving it hard to figure out what is going where.
So let's say I have a database called Attendance on SQL server localhost. Inside is a table called employees which consists of the columns: employee_id, name_last, name_first, and points. The first three are varchar and the last is a decimal(2,1). Using Visual Studio for Visual Basic, I've created a program which contains several textboxes. The user enters the employee id which becomes var_empid when they hit the Load button.
How would I proceed so that the program executes an SQL query which pulls the name_last and name_first from the table where the employee_id matches the var_empid as input by the user and puts that data into the var_last_name and var_first_name variables?
Secondly, if the user entered into other textboxes information that became var_empid, var_last_name, var_first_name and then clicked the Add Employee button, how I would i proceed so that the information added by the user is written to the SQL table?
For clarification, moving data between textboxes and variables isn't the problem. I can do that all day. It's moving the variables between the VB and SQL that is causing me problems. I realize this is basic stuff, but any help would be great.
To answer your second request, try to execute the following query:
Dim Query = "INSERT INTO Attendance (name_first, name_last, points) VALUES(var_first_name, var_last_name, var_points)"
Note that i did not insert the var_empid because if you have created your table correctly, this id should be an auto-generated primary key that increments itself automatically.
It's not easy to start because at first you see so many different names and technologies and they all seem to promise, more or less, the same thing. Especially if you want to build a database-connected application, and start doing basic stuff like CRUD operations (inserts, updates, deletes...), it's easy to get confused.
Start reading here about ADO.NET Architecture. You hopefully will understand something more about DataSet, Linq To SQL, Entity Framework, but probably not much. This is why I strongly suggest to take a few days and slowly watch the tutorial videos by Beth Massi, on VB.Net and Data.
On this page, How Do I Videos for Visual Basic, you will find a lot of useful information to start building simple but very effective applications and database.
Be sure to watch Forms over Data Video Series, Visual Basic 2008 Forms over Data Videos (using DataSets) and then Language Integrated Query (LINQ) Series (using LINQ To SQL where you'll understand why in your vb.net application your object variables have the same name as your database columns). FInally you can take a look at the Entity Framework tutorial (that you will find very similar to Linq To SQL).
After these basic tutorials you'll be able to choose your path and start programming. Once you grasp the basic concepts it's a lot easier to search and understand what you find on the internet.
This is more than what you asked for, because I'm trying to push you into some good practices at the same time:
Public Class Employee
Public Property ID As String
Public Property Points As Double 'why is this a decimal(2,1)?
Public Property LastName As String
Public Property FirstName As String
Public Shared Function FromDataRow(ByVal data As IDataRecord) As Employee
Dim result As New Employee()
result.ID = CDbl(data("ID"))
result.LastName = CStr(data("LastName"))
result.FirstName = CStr(data("FirstName"))
Return result
End Function
End Class
Public Module DataLayer
'Check www.connectionstring.com for more info on connection strings
Private Property ConnectionString As String = "database connection string here"
Private Iterator Function GetRecords(ByVal sql As String, ByVal addParams As Action(Of SqlParameterCollection)) As IEnumerable(Of IDataRecord)
Using cn As New SqlConnection(ConnectionString), _
cmd As New SqlCommand(sql, cn)
addParams(cmd.Parameters)
cn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read)
Yield Return rdr
End While
End Using
End Using
End Function
Private Function GetRecords(Of T)(ByVal sql As String, ByVal addParams As Action(Of SqlParameterCollection), ByVal translate As Function(Of IDataRecord, T)) As IEnumerable(Of T)
Return GetRecords(sql, addParams).Select(translate)
End Function
Public Function GetEmployeeData(ByVal EmployeeID As String) As Employee
Dim sql As String = _
"SELECT employee_id, name_last, name_first " & _
"FROM employees " & _
"WHERE employee_id= #ID"
Return GetRecords(sql, _
Sub(p) p.Add("#ID", SqlDbType.NVarChar, 10).Value = EmployeeID, _
Employee.FromDataRow).First()
End Function
End Class

Returning a DataTable in WCF and .NET

Thanks William, that was the ticket. Had to assign the name property on both ends [DataTable.TableName].
On a side note here: There appears to be some school of thought (no offense Marc) that the following statement is always true:
"Everything in the world can and should be made into an object."
It is, simply, not always true. There are cases where you cannot cram your 'object' into any cookie-cutter or class no matter how you try to customize it. For me to objectize this beast, I'd have to create roughly 4,000 objects. I don't have time to do that, and yet this project must run as a service. Frankly I think the developers at MickeySoft need to get out more into the real world, and see for themselves that although theory is great it does not represent true-life challenges. I am all for working with objects for the obvious benefits, but there is a reality that universals do not exist. In my trade, even 'most of the time' rarely happens.
Before they push out a new technology set, and cease support of an old one, they need to make sure that the new technology has the same capabilities the old one had.
For the record: The people who believe the above statement to be true are also the same people who would refuse to take the project I'm currently working on.
Just the same -- thank you both for your time, efforts and opinions!
I'm trying to create a WCF function that will return a table to my console testing app. I am a total noob. The data is 2-dimensional and looks like this:
23 Letter
42 Another Letter
43 Document
...
Here's what I'm trying to do:
<ServiceContract()> _
Public Interface ILetterWriter
<OperationContract()> _
Function GetLetter(ByVal LetterID As String, ByVal StateID As String, ByVal CompID As String, ByVal tblVar As DataTable) As String
<OperationContract()> _
Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable
End Interface
Public Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable Implements ILetterWriter.GetLetterNames
Dim SQLCon As New SqlClient.SqlConnection
Dim SQLCmd As New SqlClient.SqlCommand
'Connect to the database
SQLCon.ConnectionString = "Data Source=VMSQL08-SRV1;Initial Catalog=DotNetDev;User ID=aiis_pgmr;Password=ag58102;"
SQLCon.Open()
'Grab the stored procedure, which returns the letter names
SQLCmd.CommandText = "sp_GetLetters"
SQLCmd.CommandType = CommandType.StoredProcedure
SQLCmd.Connection = SQLCon
'Pass the parameters
SQLCmd.Parameters.AddWithValue("#LetterType", DepartmentType)
'Execute the stored procedure, fill the datatable from a data adapter
GetLetterNames = New DataTable
GetLetterNames.Load(SQLCmd.ExecuteReader)
'Shut it down
SQLCmd.Dispose()
SQLCon.Close()
SQLCon.Dispose()
End Function
...Of course, it won't work. I just need to get the WCF to pass a basic table to my console application. The execute SQL seems to work just fine, I just can't get the data back to my application.
Any help would be greatly appreciated.
Thanks,
Jason
I agree with the other poster.
However, if you are returning a DataTable, you have to set the "Name" property of the DataTable if you want to return it from a WCF Service.