Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementing a Provider Independent Data Access Layer in .NET

0.00/5 (No votes)
3 Nov 2003 8  
This article tells how you can implement a DAL which is provider independant.

Introduction

For applications built using Microsoft DNA, ADO provided one of the easiest ways of data access. ADO gave developers, COM based API for data access and using ADO components, a multitude of data sources could be accessed. The main reason for this was that ADO used the OLEDB provider internally to connect to data sources and more importantly there existed an OLEDB provider for ODBC. This meant that developers could use ADO to connect to ODBC sources using DSNs in their connection strings as well.

Data access in ADO.NET is not quite the same. Unlike ADO which just used OLEDB, ADO.NET provides many independent providers targeted to specific data sources. For example, the SQLClient provider deals specifically with SQL Server 7 and SQL Server 2000 as the data sources. The ODBC.NET provider deals with access to data sources using ODBC and DSNs. Likewise the OLEDB.NET provider is used to connect to OLEDB data sources. Also, there exists a separate .NET provider for Oracle databases.

Having independent providers for data access has the advantage that each provider is tuned for a specific data source and provides the most efficient access for that data source. For example, SQLClient is by far, the most efficient way of accessing SQL Server 7 or 2000 data sources. But the disadvantage here is that developers may have to decide on a particular provider and the generic way of access is not as easy as in ADO.

Now, what if the decision of selecting a provider is taken as late as application runtime? How should the data access layer (let�s call it DAL) be implemented so that applications that use the DAL be unaware of the provider being used and still work seamlessly with the DAL? This article deals with the design and implementation of the DAL which answers the above questions.

Designing the Provider independent DAL

The key to programming a provider independent DAL is to use some of the interfaces provided by the System.Data namespace and these interfaces are implemented by all providers. For example, there is an interface called IDbCommand which represents a SQL statement to be executed against a data source. Providers like SQLClient and OLEDB.NET, all implement this interface in their respective command classes. Shown in the table below are the interfaces that are typically used in a DAL. The table also gives the class of the provider which implements this interface:

Interface SQLClient Class ODBC.NET Class OLEDB Class Description
IDbConnection SqlConnection OdbcConnection OledbConnection Represents a connection to the database
IDbDataAdapter SqlDataAdapter OdbcDataAdapter OleDbDataAdapter Represents a set of command related properties that are used to work with a DataSet
IDbCommand SqlCommand OdbcCommand OleDbCommand Represents a SQL statement (or command) to be executed against a data source
IDataParameter SqlParameter OdbcParameter OleDbParameter Represents a parameter to the command object
IDataReader SqlDataReader OdbcDataReader OleDbDataReader Represents one or more result sets which can be accessed in read only, forward only manner
IDbTransaction SqlTransaction OdbcTransaction OleDbTransaction Represents a transaction to be performed against a data source

Note: The Oracle .NET data provider also implements these interfaces.

Now, let�s see how these interfaces can be used with a simple example:

Private Function RunCommandReturnReader(ByRef Cmd As IDbCommand, 
                    ByRef conn As IDbConnection ) As IDataReader 
    Cmd.Connection = conn 
    Cmd.Connection.Open() 
    Return Cmd.ExecuteReader(CommandBehavior.CloseConnection) 
End Function

Shown above is a function that executes a Command and returns a DataReader object. Note that the function takes two arguments of types IDbCommand and IDbConnection respectively and returns an object of type IDataReader.

The function shown above was just a private utility function. In the later sections of this article, I shall provide the implementation of the function that is ultimately exposed by the DAL that uses this utility function to return a DataReader object.

Provider Factory

The provider factory is a class which exposes methods that return objects of the required type to the caller. The data access class uses the provider factory to obtain command, connection objects etc. The data access class is shielded from the actual type of the objects. Given below is a skeleton of the ProviderFactory class:

Imports Microsoft.Data.Odbc
Imports System.Data.SqlClient
Imports System.Data.OleDb
Imports System.Configuration

Public Class ProviderFactory
    Public Enum EnumProviders
        ODBC
        SQLClient
        OLEDB
    End Enum

    Public Shared Function GetConnection()
        As IDbConnection

        Select Case GetProvider()
        Case EnumProviders.ODBC
            Return New OdbcConnection()
        Case EnumProviders.SQLClient
            Return New SqlConnection()
        Case EnumProviders.OLEDB
            Return New OleDbConnection()

        End Select

    End Function 

    Public Shared Function GetProvider()
        As EnumProviders 

        Dim Idt As IDictionary = _           
          CType(ConfigurationSettings.GetConfig("AppProvider"), IDictionary)
        Return CType(Idt("Provider"), EnumProviders) 

    End Function 
End Class

The ProviderFactory class shown above includes an enumeration EnumProviders, of the supported providers. In this case, three providers are supported: SQLClient, OLEDB.NET and ODBC.NET. Also exposed is a method GetConnection that returns a connection object based on what is set in the application configuration file. If the developer has set it to 0, then GetConnection returns an ODBCConnection object and if it is set to 2, then an OledbConnection object is returned. In the above example, we had implemented a method to return a connection object. In a similar fashion, the provider factory can implement methods returning Command, DataReader, DataAdapter objects etc.

Using the Provider Factory

Understand that it is the provider factory that takes care of returning the objects of the right type. So, the clients of the provider factory work only with interfaces. In the example shown below, we have a function that executes a command and returns a DataReader. Note that we had dealt with the helper function RunCommandReturnReader earlier:

Public Function RunSQLReturnDataReader(ByVal strSQL As String,ByVal 
            cmdtype As CommandType) As IDataReader 
    Dim cmd As IDbCommand = ProviderFactory.GetCommand(strSQL, cmdtype) 
    Dim conn As IDbConnection = ProviderFactory.GetConnection("") 
    Return RunCommandReturnReader(cmd, conn) 
End Function

About the source

The source zip file includes a VB.NET class library project containing simple data access routines. This helper may not suffice to all the requirements of a DAL of an enterprise application, but definitely illustrates the concept of the class factory pattern of application design.

V1.0 of the source code was very naive and didn't do anything much. The source here was compiled with v1.0 of the framework, where ODBC.NET provider was a separate download. In v1.1 of the framework, all the ODBC classes are now a part of the System.Data.ODBC namespace, which ships with the framework.

Many of the readers wanted a sample application using the DAL. Well, here it is, the V2.0 of the source has a simple application that shows some instances of how the DAL can be used. Among the many things added to the DAL are:

  • ADO.NET transaction support
  • Parameter caching (Based on the data access application block)
  • Parameter discovery (Based on the data access application block)

The last two should be of some interest to developers. In the sample source, I haven't really exploited the use of parameter caching and discovery. In real world applications, these can be of great use to the application and can boost the performance of the application while reducing the coding effort at the same time. For example, we can have a Windows service hosting a cache of parameters which are discovered and updated periodically. With this, the application developers need not write any code to build the parameter collection and just rely on the service for the same. Of course, you will require a remoting infrastructure to talk to the service, but this too would be very efficient.

A note to the readers: The method signatures in the source code may be slightly different from the ones in this article text. The concept however, remains the same.

Other resources

I would like to recommend two great resources for Data Access in .NET:

Conclusion

Having a provider independent DAL definitely gives an application more flexibility in terms of selecting the right provider at runtime and also prevents other parts of the application from locking down to a particular provider.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here