HomeASP.NET Database Independent Development using ASP...
Database Independent Development using ASP.NET 2.0: Dealing with Stored Procedures
This is the second part of a two-part article. In the previous part we focused on developing database independent applications using ASP.NET 2.0. In this part, we will extend the functionality of the data access layer to the stored procedure level.
I strongly suggest that you go through the first part of this series, if you are new to database independent development in .NET 2.0.
The entire solution (source code) for this article is available as a free download (in the form of a zip). All the applications in this series have been developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Standard Edition together with Microsoft SQL Server 2005 Express Edition and Oracle 10g Express Edition as the database. I didn't really test any of the code in any other tools/IDEs/servers/editions/versions. If you have any problems, please feel free to post in the discussion area.
Defining a class to hold the details of a stored procedure parameter
When we deal with stored procedures, we quite often work with parameters as well. To hold the information of a parameter, I would like to define my own class as follows:
Imports System.Data.Common
PublicClass SPParameter
Public ParameterName AsString Public ParameterValue AsObject Public ParameterDataType As DbType Public ParameterSize AsInteger Public ParameterDirectionUsed As ParameterDirection
The above class simply holds the name and value of the stored procedure parameter along with other information like type, size and direction (IN, OUT, IN OUT or RETURN).
Developing a separate class to hold parameter information is very helpful if you would like to scale your layer (data access layer) to meet multi-tier architectures and requirements. For example, you can make the above class serializable by modifying it as follows:
A stored procedure may have more than one parameter to be accepted. Before executing a stored procedure, we need to "cache" all those parameters in an object to serve the same while executing the stored procedure.
In this scenario, I would like to have the "cache" be determined as an "ArrayList" object. The declaration of that object is as follows:
Dim alSPParamCache As ArrayList = Nothing
I would like to have each of the parameters to be added to the above "cache" object using a separate method as follows:
PublicSub SPParameterAdd(ByVal ParamName AsString, ByVal ParamValue AsObject, OptionalByVal ParamDirection As ParameterDirection = ParameterDirection.Input, OptionalByVal ParamDataType As DbType = Nothing, OptionalByVal ParamSize AsInteger = Nothing) If alSPParamCache IsNothingThen alSPParamCache = New ArrayList EndIf Dim objParam AsNew SPParameter With objParam .ParameterName = ParamName .ParameterValue = ParamValue .ParameterDirectionUsed = ParamDirection .ParameterDataType = ParamDataType .ParameterSize = ParamSize EndWith alSPParamCache.Add(objParam) EndSub
The entire parameter cache must be cleared after successful execution of a stored procedure (or even manually when necessary). The following is the method which is meant for that:
The previous sections were dedicated to dealing with stored procedure parameters. Now, we shall deal with the execution of a stored procedure. The following is the method which executes a stored procedure based on the parameters added to the cache:
PublicSub SPExecute(ByVal SPName AsString) Dim cmd As DbCommand = GetDBCommand() AddSPParamFromCache(cmd) cmd.CommandText = SPName cmd.CommandType = CommandType.StoredProcedure Try cmd.Connection.Open() cmd.ExecuteNonQuery() Catch ex As Exception ThrowNew Exception(ex.Message & "-->Stored Procedure:" & SPName) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() SPParameterClearCache() EndTry EndSub
The only new statement from the above code is the following:
AddSPParamFromCache(cmd)
It is a separate method from the same class, which is defined as follows:
PrivateSub AddSPParamFromCache(ByRef cmd As DbCommand) If alSPParamCache IsNothingThen ExitSub'no parameters to add EndIf ForEach objSPParam As SPParameter In alSPParamCache Dim p As DbParameter = cmd.CreateParameter With p .ParameterName = objSPParam.ParameterName .Value = objSPParam.ParameterValue .Direction = objSPParam.ParameterDirectionUsed .DbType = objSPParam.ParameterDataType .Size = objSPParam.ParameterSize EndWith cmd.Parameters.Add(p) Next EndSub
Before executing the stored procedure, we need to add all the parameters (available as part of the cache) to the command object. The above method simply adds all the parameters available in the cache "alSPParamCache" (of type ArrayList) to the Command object passed to it.
Once we know about executing the stored procedures using parameters, we can easily retrieve the entire information from the database. Now, let us bring a set of rows from the database in the form of a dataset, by executing a stored procedure as follows:
PublicFunction SPgetDataSet(ByVal SPName AsString) As DataSet Dim ds AsNew DataSet Dim da As DbDataAdapter = GetDBDataAdapter() Dim cmd As DbCommand = GetDBCommand() AddSPParamFromCache(cmd) cmd.CommandText = SPName cmd.CommandType = CommandType.StoredProcedure da.SelectCommand = cmd Try da.Fill(ds) Catch ex As Exception ThrowNew Exception(ex.Message & "-->Stored Procedure:" & SPName) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() da.Dispose() SPParameterClearCache() EndTry Return ds EndFunction
The above method returns a dataset which is filled using a DataAdapter. You can even return a "DataTable" object using a separate method as follows:
PublicFunction SPgetDataTable(ByVal SPName AsString) As DataTable Dim dt AsNew DataTable Dim da As DbDataAdapter = GetDBDataAdapter() Dim cmd As DbCommand = GetDBCommand() AddSPParamFromCache(cmd) cmd.CommandText = SPName cmd.CommandType = CommandType.StoredProcedure da.SelectCommand = cmd Try da.Fill(dt) Catch ex As Exception ThrowNew Exception(ex.Message & "-->Stored Procedure:" & SPName) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() da.Dispose() SPParameterClearCache() EndTry Return dt EndFunction
In other words, you can further reduce the above code (which returns the "DataTable" object) by rewriting it as follows:
PublicFunction SPgetDataTable(ByVal SPName AsString) As DataTable Dim ds As DataSet = SPgetDataSet(SPName) If ds.Tables.Count = 0 Then ReturnNothing Else Return ds.Tables(0) EndIf EndFunction
In the above code, I am simply reusing the existing method "SPgetDataSet" to decrease several lines of code!
Now that we know about returning a set of rows, it is time to retrieve a single row. Let us go through the following method:
PublicFunction SPgetDataRow(ByVal SPName AsString) As DataRow Dim dt As DataTable = SPgetDataTable(SPName) If dt.Rows.Count > 0 Then Return dt.Rows(0) Else ReturnNothing EndIf EndFunction
You can observe from the above method that I am simply reusing the previous method "SPgetDataTable" (which returns a set of rows) and returning only the first row. Returning a single value is a bit different from above. Let us consider the following method:
PublicFunction SPgetGetValue(ByVal SPName AsString) AsString Dim cmd As DbCommand = GetDBCommand() AddSPParamFromCache(cmd) cmd.CommandText = SPName cmd.CommandType = CommandType.StoredProcedure Try cmd.Connection.Open() Return cmd.ExecuteScalar & "" Catch ex As Exception ThrowNew Exception(ex.Message & "-->Stored Procedure:" & SPName) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() SPParameterClearCache() EndTry EndFunction
In the above code, you can understand that I am using the "ExecuteScalar" method of the Command object to improve the performance while retrieving a single value from the database.
To execute the methods in the DAL, you need to create an object as follows:
Dim db As DALib.DataAccess
ProtectedSub Page_Load(ByVal sender AsObject, ByVal e As System.EventArgs) HandlesMe.Load Dim ProvName AsString = ConfigurationManager.AppSettings("Provider- Type").ToString Dim ConnString AsString = ConfigurationManager.ConnectionStrings ("ConnectionString").ConnectionString.ToString db = New DALib.DataAccess(ProvName, ConnString) EndSub
Prior to the above, make sure that your web.config file is equipped with the proper provider type and connection string as follows:
You can expect the third (and final) part of this series soon. I hope you enjoyed the article and any comments, suggestions, feedback, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com