HomeASP.NET Database Independent Development using ASP...
Database Independent Development using ASP.NET 2.0
This article shows you how to develop database independent applications using ASP.NET 2.0. I shall also show you how to develop a simple database independent data access layer using ADO.NET 2.0.
A downloadable file for this article is available here.
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 editions 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.
Provider independent model in ADO.NET 2.0
ADO.NET internally works with .NET data providers (or .NET data bridge providers) to connect to and access data from different kinds of data sources (including databases). The same data provider model existing in ADO.NET 1.1 is further enhanced in ADO.NET 2.0 (with a few factory classes) to leverage the flexibility of developing database independent applications.
The purpose of a factory class is to provide an interface for creating families of related objects, with or without specifying their concrete (method implementation) classes. If the factory class is created without one or more implementations of methods, we call it an abstract factory class.
The provider-independent programming model in ADO.NET 2.0 revolves around the classes in the System.Data.Common namespace. There are mainly two new factory classes that implement the provider-independent model (with the same namespace):
DbProviderFactories
DbProviderFactory
The DbProviderFactories class is mainly used to enumerate all .NET data providers installed on your machine. Using the same class, we can also create instances related to a specific provider (to access databases specific to that provider). These instances are nothing but the objects of the DbProviderFactory class.
Using the DbProviderFactory class, we can create Connection, Command, DataAdapter and other objects. These objects will be provider specific (like SQLConnection, OracleConnection, ODBCConnection etc.), but will be returned into a "commons" like parent classes called DBConnection, DBCommand, DBDataAdapter etc. which are available as part of System.Data.Common namespace.
The upcoming sections will show you how to develop a database independent data access layer using ADO.NET 2.0
Those two are the variables which hold the .NET data provider to use and the connection string to connect. For example, _DBProviderName can be assigned with "System.Data.SqlClient" (as a string itself) to work with Microsoft SQL Server specific .NET data provider. Similarly, you can even assign "Oracle.DataAccess.Client" to work with ODP.NET (or Oracle's .NET data provider to access Oracle).
Going a bit further down, we have the following constructor:
The above constructor receives ProviderName (ex: System.Data.SqlClient as a string) and ConnectionString as parameters, and checks whether or not the provider is installed on the machine. If the provider is not installed (say ODP.NET is not installed but you are trying to use Oracle.DataAccess.Client), then it raises an exception.
If the .NET data provider is already installed, then it creates a factory object (based on the provider specification), which can be used later to create other objects like connections, commands etc. specific to that provider. Proceeding further down, we have the following:
PublicFunction GetDBConnection() As DbConnection Dim dbConn As DbConnection = GetDBProviderFactory.CreateConnection dbConn.ConnectionString = _ConnectionString Return dbConn EndFunction
The above function simply creates a Connection object (based on the .NET data provider selected by the factory), assigns the connection string and returns the same Connection object to the calling method. We can use this Connection object later to create other objects like Command, DataAdapter and so forth.
Going further down, we have two more function which create and return Command/DataAdapter objects to the calling methods:
PublicFunction GetDBCommand() As DbCommand Dim dbCmd As DbCommand = GetDBProviderFactory.CreateCommand dbCmd.Connection = GetDBConnection() Return dbCmd EndFunction
PublicFunction GetDBDataAdapter() As DbDataAdapter Dim dbAdap As DbDataAdapter = GetDBProviderFactory.CreateDataAdapter Return dbAdap EndFunction
The following is the skeleton of the DataAccess class, which works with the DBFactory class (listed in the previous section) and acts as a helper class to the business logic classes:
This section explains the skeleton provided in the previous section. Before looking at the methods, if you can observe the declaration of the class DataAccess, it is derived (or inherited) from the DBFactory class. That means all the methods of the DBFactory class can be accessed as part of the DataAccess class.
In simple terms, we can get Connection, Command and DataAdapter objects from the base class DBFactory itself. Furthermore, you can also instantiate and work with the DBFactory class directly!
The following is the method which is used to execute any SQL statement in the database:
PublicSub SQLExecute(ByVal sql AsString) Dim cmd As DbCommand = GetDBCommand() cmd.CommandText = sql cmd.CommandType = CommandType.Text Try cmd.Connection.Open() cmd.ExecuteNonQuery() Catch ex As Exception ThrowNew Exception(ex.Message & "-->" & sql) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() EndTry EndSub
In the above method, I am simply receiving the Command object from the GetDBCommand method available in the DBFactory class. Once the Command object is available, the process of executing the SQL statement is exactly the same as the traditional ADO.NET 1.1 approach.
The following is the method which retrieves a set of rows in the form of a data table:
PublicFunction GetDataTable(ByVal sqlSELECT AsString) As DataTable Dim dt AsNew DataTable Dim da As DbDataAdapter = GetDBDataAdapter() Dim cmd As DbCommand = GetDBCommand() cmd.CommandText = sqlSELECT cmd.CommandType = CommandType.Text da.SelectCommand = cmd Try da.Fill(dt) Catch ex As Exception ThrowNew Exception(ex.Message & "-->" & sqlSELECT) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() da.Dispose() EndTry Return dt EndFunction
In the above function, you can observe that I used both DataAdapter and Command objects (from the methods of DBFactory class) to fill the DataTable object.
The following is the method which retrieves a single row in the form of a data row:
PublicFunction GetDataRow(ByVal sqlSELECT AsString) As DataRow Dim dt As DataTable = GetDataTable(sqlSELECT) If dt.Rows.Count > 0 Then Return dt.Rows(0) Else ReturnNothing EndIf EndFunction
The above function works with the previous GetDataTable function to return a set of rows. The following is the method which retrieves a single value in the form of a string from the database:
PublicFunction GetValue(ByVal sqlSELECT AsString) AsString Dim cmd As DbCommand = GetDBCommand() cmd.CommandText = sqlSELECT cmd.CommandType = CommandType.Text Try cmd.Connection.Open() Return cmd.ExecuteScalar & "" Catch ex As Exception ThrowNew Exception(ex.Message & "-->" & sqlSELECT) Finally If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() EndIf cmd.Dispose() EndTry EndFunction
You can further enhance the above classes with the support of transactions, store procedures etc. You can find an ASP.NET application for testing along with the above classes as part of the free download.
I hope you enjoyed the article and any comments, suggestions, feedback, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com