If you’ve ever has to refactor a database or business objects that interact with strongly typed datasets, you’ll quickly understand why they’re evil. Pure evil. This extra meaty slab walks through creating a data access layer, and how to use it.
Steps to Creating a DAL Object in VB.Net
1. Start a new project or add to an existing solution. In order to see the solution itself in the Solution Explorer, you may need to set it to visible somewhere under Tools, Options.
2. Create the new project of type “Class Library”. Name it “DataLayer”.
3. Add a setting to DataLayer by double clicking “My Project” and navigating to the Settings tab. Name it cnn, fill in a value, and scope it to user.
4. Add references to the project (by right clicking the project, then Add Reference…) for System.Data and System.Configuration.
5. Rename Class1.vb to DAL.vb.
6. Place this code in it:
Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration
Imports System.Text
Public NotInheritable Class DAL
Implements IDisposable
Private sqlCn As New SqlConnection()
Private _LastException As SqlException
Public ReadOnly Property LastException() As SqlException
Get
Return _LastException
End Get
End Property
Public Sub New(ByVal cnnString As String)
MyBase.New()
Me.OpenConnection(cnnString)
End Sub
Public Overrides Function ToString() As String
If Me.sqlCn Is Nothing Then
Return ""
Else
Return Me.sqlCn.ToString
End If
End Function
Private Sub OpenConnection(ByVal cnnString As String)
If sqlCn.State <> ConnectionState.Open Then
Try
If Me.sqlCn Is Nothing Then
Me.sqlCn = New SqlConnection
End If
sqlCn.ConnectionString = cnnString
sqlCn.Open()
Catch sqlEx As SqlException
Me._LastException = sqlEx
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End If
End Sub
Private Sub CloseConnection()
If sqlCn.State = ConnectionState.Open Then
sqlCn.Close()
End If
Me.Dispose()
End Sub
Public Function GetDataTable(ByVal SQL As String) As DataTable
Dim inv As New DataTable()
Try
Using cmd As New SqlCommand(SQL, Me.sqlCn)
Dim dr As SqlDataReader = cmd.ExecuteReader()
inv.Load(dr)
dr.Close()
End Using
Me.CloseConnection()
Return inv
Catch sqlEx As SqlException
Return Nothing
Catch ex As Exception
Return Nothing
End Try
End Function
Public Function RunNonQuery(ByVal SQL As String) As Object
Try
If sqlCn.State = ConnectionState.Closed Then
sqlCn.Open()
End If
Using cmd As New SqlCommand(SQL, Me.sqlCn)
cmd.CommandTimeout = 0
cmd.ExecuteNonQuery()
End Using
Catch sqlEx As SqlException
Return sqlEx
Catch ex As Exception
Return ex
End Try
Me.CloseConnection()
Return Nothing
End Function
Public Function ExecuteProc(ByVal SP As String, ByVal params As Array) As Object
Dim SQL As New StringBuilder
SQL.Append("EXEC dbo.")
SQL.Append(SP)
SQL.Append(" ")
For index As Integer = 0 To params.Length - 1
SQL.Append(params(index))
SQL.Append(", ")
Next
SQL.Remove(SQL.Length - 2, 2)
Try
If sqlCn.State = ConnectionState.Closed Then
sqlCn.Open()
End If
Using cmd As New SqlCommand(SQL.ToString, Me.sqlCn)
cmd.CommandTimeout = 0
cmd.ExecuteNonQuery()
End Using
Catch sqlEx As SqlException
Return sqlEx
Catch ex As Exception
Return ex.ToString
End Try
Me.CloseConnection()
Return Nothing
End Function
Public Function PerformTransaction(ByVal OrderedCmdList As Generic.SortedList(Of Integer, String)) As Boolean
Dim trans = sqlCn.BeginTransaction()
Try
For i As Integer = 0 To OrderedCmdList.Count - 1
Using cmd As New SqlCommand(OrderedCmdList(i), Me.sqlCn)
cmd.Transaction = trans
cmd.CommandTimeout = 0
cmd.ExecuteNonQuery()
End Using
Next
trans.Commit()
Return True
Catch sqlEx As SqlException
Me._LastException = sqlEx
trans.Rollback()
Return False
Finally
Me.CloseConnection()
End Try
End Function
Public Sub PerformBulkCommands(ByVal OrderedCmdList As Generic.SortedList(Of Integer, String), ByVal IgnoreErrors As Boolean)
For i As Integer = 0 To OrderedCmdList.Count - 1
Try
Using cmd As New SqlCommand(OrderedCmdList(i), Me.sqlCn)
cmd.CommandTimeout = 0
cmd.ExecuteNonQuery()
End Using
Catch sqlEx As SqlException
Me._LastException = sqlEx
If IgnoreErrors = False Then
Exit For
End If
End Try
Next
Me.CloseConnection()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Me.sqlCn = Nothing
GC.Collect()
End Sub
End Class
Using the DAL Object
1. Remove references to System.Data in the project that is to be changed.
2. Add a reference to DataLayer (right click the project, Add Reference…, Projects tab)
3. Remove the “Imports System.Data”, if you have one.
4. Add: “Imports DataLayer”.
5. If you want to consume a data table:
Private Sub ShowRows()
Dim MyDAL As New DAL(cnn)
Dim strSQL As String = "SELECT * FROM dbo.EmployeeMaster"
Dim dt As DataTable = MyDAL.GetDataTable(strSQL)
If retval IsNot Nothing Then
For curRow As Integer = 0 To dt.Rows.Count - 1
'do something
Next
Else
MsgBox("You did something bad, and now I'm mad at you.", _
MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, "Error")
End If
End Sub
6. If you want to run a stored procedure that does not return a dataset:
Private Sub RunProc()
Dim retval As Object = Nothing
Dim MyDAL As New DAL(cnn)
Dim strSQL As String = "EXEC dbo.SomeProCedure"
retval = MyDAL.RunNonQuery(strSQL)
If retval IsNot Nothing Then
MsgBox(retval.ToString, MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, "Error")
End If
End Sub
7. If your proc has params, you do this:
Private Sub RunProc()
Dim retval As Object = Nothing
Dim MyDAL As New DAL(cnn)
Dim SP As String = "SomeProcedure"
Dim params(2) As String
params(0) = Shift.ToString
params(1) = "'" & ProdDate & "'"
retval = MyDAL.ExecuteProc(SP, params)
If retval IsNot Nothing Then
MsgBox(retval.ToString, MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, "Error")
End If
End Sub
Last Thoughts
The DataLayer DLL can now be used anywhere.
This paves the way to building business objects that hydrate themselves. Instead of binding form widgets to datasets, you can new up an object and bind the widget to the
object. This is good because it gets data access out of the UI.
note: edited 11 Nov 2008 – added more error handling.
October 6, 2008 at 9:12 am
[...] Note: This technique works for stronly typed datasets, but a better alternative is to use a data access layer object. [...]
October 16, 2008 at 10:01 am
I want to emphasize that, by itself, this is only a stepping stone. The final product shouldn’t have TSQL statements in it! In my opinion, the “right way” to get data into applications is call stored procs, and the only place we should be doing that is in business objects, not form code.
January 19, 2009 at 10:17 am
My Dataloader
January 28, 2009 at 8:46 am
[...] and of course, DataLayer [...]