Working with MS Access Stored Procedures in VB.NET. Part 1
2/27/2002
Article source code: msaccess_sp.zip
Introduction
In the more recent releases of Microsoft Access, great effort has gone into making this product a full-featured relational database system. Stored procedures, a functionality usually associated with enterprise database systems such as SQL Server, can now be found in Access. Stored procedures in Access have been available since Access 2000 and are native to the Jet 4 Database Engine. If you're accustomed to using stored procedures in SQL Server, then you'll be right at home with how they're used in Access. However there are some limitations to keep in mind. I'll discuss those later on.
This article will be broken down into two parts. Part one will describe in detail how to create stored procedures in Access using ADO.NET and Visual Basic.NET. Part two will demonstrate how to utilize the stored procedures created in part one by assembling a data access tier that can be modelled and used in your own applications. The code in this article has been tested using Access 2002, although it should also work with Access 2000.
How do stored procedures work in Access?
Unlike other objects in Access, stored procedures have no interface and cannot be created or run through the Access User Interface. The way to get them into your database is to simply code them. I'll show how that's done in ADO.NET.
When a stored procedure is added to an Access Database, the Jet Engine reworks the stored procedure syntax into a query object. To an Access developer this may sound like unnecessary work to code a query. However, it does have its advantages. Consider an application that has to break out into different versions when maintaining both an Access Database and a SQL Server Database. Using stored procedures will make it easier to write the code for the database tier of the application as the program will change very little between the different versions.
Creating Stored Procedures
To demonstrate, I'll first show how to create the SQL statements to create stored procedures. At the end of the article I'll show the entire code needed to run these statements against the database. Using the Northwind database that comes with Access, four stored procedures will be created. Focusing on the Products table for all of them, let's start off with the easiest one; select all data of each row in the table. To create the stored procedure, execute the following SQL statement against the database:
"CREATE PROC procProductsList AS SELECT * FROM Products;"
The statement: "CREATE PROC procCustomerList" is the part that actually creates the stored procedure. The part following "AS" can be any valid SQL Statement.
Often in a stored procedure you'll want to pass a value to be used in the query. Consider that you may want to delete a record based on a particular ProductID. The following stored procedure shows how to do just that:
"CREATE PROC procProductsDeleteItem(inProductsID LONG)" & _
"AS DELETE FROM Products WHERE ProductsID = inProductsID;"
On the first line, notice the parenthesis right after the CREATE PROC declaration. There is a parameter defined as a Long value. This is where you add the variable to delete the record in question.
The next two statements show how to create an add and an update stored procedure for the Products table respectively. Note that not all fields are included for the sake of brevity:
"CREATE PROC procProductsAddItem(inProductName VARCHAR(40), " & _
"inSupplierID LONG, inCategoryID LONG) " & _
"AS INSERT INTO Products (ProductName, SupplierID, CategoryID) " & _
"Values (inProductName, inSupplierID, inCategoryID);"
"CREATE PROC procProductsUpdateItem(inProductID LONG, " & _
" inProductName VARCHAR(40)) " & _
"AS UPDATE Products SET ProductName = inProductName " & _
" WHERE ProductID = inProductID;"
Notice that a comma separates each parameter when more than one is specified.
Limitations
There are some limitations you may encounter here, especially if you're used to the power of SQL Server.
Output parameters cannot be used.
Don't use the @ character. The @ character is often used in Transact SQL (SQL Server), where it represents a local variable. Access doesn't always convert this character and will sometimes leave it out. This can cause esoteric bugs which can lead to premature hair loss.
Temporary tables are not available in Access.
I suspect many of the options available in Transact SQL are not available in Access as it's not Transact SQL compatible.
Conclusion
Hopefully, this article has provided some guidance in a nearly undocumented area of Access and Jet not yet explored by most. For more information on how the ADO.NET code works in the CreateStoredProc subroutine, see Getting Started with ADO.NET by Gurneet Singh. The following is a complete listing of all code presented in this article:
Imports System
Imports System.Data
Imports System.Data.OleDb
Module CreateSP
Sub Main()
ProductsProcs()
End Sub
' Products Stored Procs to be added to the db.
Sub ProductsProcs()
Dim sSQL As String
' procProductsList - Retrieves entire table
sSQL = "CREATE PROC procProductsList AS SELECT * FROM Products;"
CreateStoredProc(sSQL)
' procProductsDeleteItem - Returns the details (one record) from the
' JobTitle table
sSQL = "CREATE PROC procProductsDeleteItem(@ProductID LONG) AS " _
& "DELETE FROM Products WHERE ProductID = @ProductID;"
CreateStoredProc(sSQL)
' procProductsAddItem - Add one record to the JobTitle table
sSQL = "CREATE PROC procProductsAddItem(inProductName VARCHAR(40), " _
& "inSupplierID LONG, inCategoryID LONG) AS INSERT INTO " _
& "Products (ProductName, SupplierID, CategoryID) Values " _
& "(inProductName, inSupplierID, CategoryID);"
CreateStoredProc(sSQL)
' procProductsUpdateItem - Update one record on the JobTitle table
sSQL = "CREATE PROC procProductsUpdateItem(inProductID LONG, " _
& "inProductName VARCHAR(40)) AS UPDATE Products SET " _
& "ProductName = inProductName WHERE ProductID = inProductID;"
CreateStoredProc(sSQL)
End Sub
' Execute the creation of Stored Procedures
Sub CreateStoredProc(ByVal sSQL As String)
Dim con As OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
Dim da As OleDbDataAdapter
' Change Data Source to the location of Northwind.mdb on your local
' system.
Dim sConStr As String = "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data " _
& "Source=C:\Program Files\Microsoft " _
& "Office\Office10\Samples\Northwind.mdb"
con = New OleDbConnection(sConStr)
cmd.Connection = con
cmd.CommandText = sSQL
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Sub
End Module
Working with MS Access Stored Procedures in VB.NET. Part 2
4/18/2002
Article source code: msaccess_sp2.zip
Introduction
Welcome to part two of Access Stored Procedures. Part one described in detail how to create stored procedures in Access using ADO.NET and Visual Basic.NET. Part two will demonstrate how to utilize the stored procedures created in part one by assembling a Database Tier that can be modelled and used in your own applications. This article will describe in detail one implementation of a Database Tier for Visual Basic.NET.
The main purpose of the Database Tier is to provide a gateway to the database via a class module. This class module would act as the glue between the database and the application. There are two main advantages to using a data tier to access your database. You will have the ability to modify your underlying database technology (moving from MS Access to SQL Server for instance) without affecting your application in a major way. You will also be placing a control layer between your application and the database allowing you to ensure that all data is properly "cleansed". The Database Tier in .NET applications would most likely consist of a class module keeping in line with proper object-oriented coding conventions. Earlier versions of Visual Basic would employ a Standard Module to do the job.
Database Tier - Code
It's now time to roll up our sleeves and get dirty with some code. The first thing after adding an empty class declaration file is to pull in the proper .NET Framework libraries listed below.
Imports System
Imports System.Data
Imports System.Data.OleDb
The System Library is standard for most applications, and I make it a habit to include it in almost all my code modules. The System.Data library is necessary for almost all database access applications. The System.Data.OleDb is used specifically for OLEDB Database Providers to which Microsoft Access belongs to. If we were using SQL Server we'd include the custom SQL provider System.Data.SqlClient.
Then next line of code starts the definition of the Class:
Public Class DBTier
Here we've named the Class DBTier and have given it a modifier of Public, thus making it very accessible from other code modules. After the class is defined all properties are declared:
Shared connectionString As String = _
"PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Program " _
& "Files\Microsoft Office\Office10\Samples\Northwind.mdb"
Only one property is declared here as a string variable, connectionString. This variable holds the connection string for the Northwind Access Database. Declaring the variable as Shared defines it as "Class Variable". A class variable is associated with the class, not each object instantiated from the class.
After the connection string declaration you'll find there are three subroutines and one function. The function returns a dataset with a listing of all products. It calls the stored procedure procProductsList, created in part one of this article.
Next you'll find the three subroutines. There is one for each stored procedure; add, update and deletion of products. They're all similarly structured; each with a command, connection and required parameter(s) declared. As a sample, let's dissect the ProductsDeleteItem subroutine. After understanding how this subroutine works the others should be easy to digest.
To start off the routine takes in one parameter, ProductID, which is an Integer representing the Product to be deleted.
Sub ProductsDeleteItem(ByVal ProductID As Integer)
Next, all variables are declared. One for the connection, command and a parameter to be passed into the stored procedure. This parameter is the ProductID to be deleted.
Dim con As OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
Dim paramProductID As New OleDbParameter()
Command and connection objects are initialized:
con = New OleDbConnection(connectionString)
cmd.Connection = con
The paramProductID parameter properties are configured. Then the parameter is added to the command object. In this case the parameter name in the stored procedure is inProductID, it's an integer and the value is set to the ProductID passed into this subroutine.
With paramProductID
.ParameterName = "inProductID"
.OleDbType = OleDbType.Integer
.Size = 4
.Value = ProductID
End With
cmd.Parameters.Add(paramProductID)
The last part actually calls the stored procedure.
cmd.CommandText = "EXECUTE procProductsDeleteItem"
con.Open()
cmd.ExecuteNonQuery()
con.Close()
Notice that the connection object only stays open long enough to carry out the stored procedure and then closes immediately. This reduces any possible contention.
While the DBTier class included in this article clearly describes how to access the stored procedures, it would need some enhancements to become quality production code since no error handling has been added. There may also be the need to further enhance performance here.
The downloaded source code associated with this article includes the DBTier.vb file along with some very basic forms to test the actual implementation of the class.
In conclusion, I hope you have gained at least two things from these articles. One being that stored procedures are alive and well in Microsoft Access, although not without their limitations. The second thing to walk away with here is understanding the need to break down an application's data access into separate classes, subroutines and functions. This makes maintenance and upgrades much easier to implement.
Entire DBTier.vb source code:
Imports System
Imports System.Data
Imports System.Data.OleDb
' Functions and subroutines for executing Stored Procedures in Access.
Public Class DBTier
' Change Data Source to the location of Northwind.mdb on your local
' system.
Shared connectionString As String = _
"PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Program " _
& "Files\Microsoft Office\Office10\Samples\Northwind.mdb"
' This function returns a dataset containing all records in
' the Products Table.
Function ProductsList() As DataSet
Dim con As OleDbConnection
Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim sSQL As String
sSQL = "EXECUTE procProductsList"
con = New OleDbConnection(connectionString)
da = New OleDbDataAdapter(sSQL, con)
ds = New DataSet()
da.Fill(ds, "Products")
Return ds
End Function
' This Function adds one record to the Products table.
Sub ProductsAddItem(ByVal ProductName As String, _
ByVal SupplierID As Integer, ByVal CategoryID As Integer)
Dim con As OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
Dim paramProductName As New OleDbParameter()
Dim paramSupplierID As New OleDbParameter()
Dim paramCategoryID As New OleDbParameter()
con = New OleDbConnection(connectionString)
cmd.Connection = con
With paramProductName
.ParameterName = "inProductName"
.OleDbType = OleDbType.VarChar
.Size = 40
.Value = ProductName
End With
cmd.Parameters.Add(paramProductName)
With paramSupplierID
.ParameterName = "inSupplierID"
.OleDbType = OleDbType.Integer
.Size = 4
.Value = SupplierID
End With
cmd.Parameters.Add(paramSupplierID)
With paramCategoryID
.ParameterName = "inCategoryID"
.OleDbType = OleDbType.Integer
.Size = 4
.Value = CategoryID
End With
cmd.Parameters.Add(paramCategoryID)
cmd.CommandText = "EXECUTE procProductsAddItem"
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Sub
' This function Updates a specific JobTitle Record with new data.
Sub ProductsUpdateItem(ByVal ProductID As Integer, _
ByVal ProductName As String)
Dim con As OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
Dim paramProductName As New OleDbParameter()
Dim paramProductID As New OleDbParameter()
con = New OleDbConnection(connectionString)
cmd.Connection = con
With paramProductID
.ParameterName = "inProductID"
.OleDbType = OleDbType.Integer
.Size = 4
.Value = ProductID
End With
cmd.Parameters.Add(paramProductID)
With paramProductName
.ParameterName = "inProductName"
.OleDbType = OleDbType.VarChar
.Size = 40
.Value = ProductName
End With
cmd.Parameters.Add(paramProductName)
cmd.CommandText = "EXECUTE procProductsUpdateItem"
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Sub
' This function deletes one record from the Products table.
Sub ProductsDeleteItem(ByVal ProductID As Integer)
Dim con As OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
Dim paramProductID As New OleDbParameter()
con = New OleDbConnection(connectionString)
cmd.Connection = con
With paramProductID
.ParameterName = "inProductID"
.OleDbType = OleDbType.Integer
.Size = 4
.Value = ProductID
End With
cmd.Parameters.Add(paramProductID)
cmd.CommandText = "EXECUTE procProductsDeleteItem"
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Sub
End Class