Enterprise Library Wrapper Class to Implement Run-time Dynamic Connection Strings Without Using Config File





4.00/5 (3 votes)
Enterprise Library wrapper class to implement run-time dynamic connection strings without using config file
- Download EntLibWrapper class (VB file) - 3.84 KB
- Download EntLibWrapper class help (CHM file) - 18.43 KB
- Download Encryption / Decryption functions (VB file) - 718 B
- Sample web.config for Encryption / Decryption (XML file) - 738 B
Introduction
This is an Enterprise Library wrapper class to implement run-time dynamic connection strings without using config file.
Background
Enterprise Library out of the box recommends storing connection strings and provider information in a config file. Our applications generate connection strings dynamically during run-time. So I developed this enterprise library wrapper class to allow us to use the enterprise library with run-time dynamic connection strings.
Using the Code
Download the EntLibWrapper class and the Common functions.
The downloaded VB files can be included in a .NET project to use the EntLibWrapper
class. I am giving below details as to how we can use the Enterprise Library wrapper class with run-time dynamic connection strings:
I use the following Enterprise Library wrapper class to use the library with run-time generated connection strings:
Imports Microsoft.Practices.EnterpriseLibrary.Data
Imports system.Data.Common
Imports system.Data.Odbc
Imports System.Data.OleDb
Imports System.Data.OracleClient
Imports System.Data.SqlClient
''' <summary>
''' This is a wrapper class for enterprise library.
''' </summary>
''' <remarks>
''' This is a wrapper class for enterprise library to avoid web.config
''' and to make connectionStrings runtime configurable using Global.asax
''' application variables which have the connectionStrings.
''' In Global.asax you can retrieve any passwords stored in web.config
''' and assemble the connectionString
''' and store in application variables when the application starts.
''' These connection strings can then be passed in when this class
''' is instantiated along with the required provider factory to use.
''' appSetting in web.config and connection strings stored in
''' Application variables can be encrypted for security purposes.
''' </remarks>
Public Class EntLibWrapper
Private db As Database
Private dbCommand As DbCommand
Private dbConn As DbConnection
Private dbread As IDataReader
Private conString As String
Private dbProvider As enumProviderFactory
Private dbRowsAffected As Integer
Private dbIdentity As Integer
''' <summary>
''' Enumeration of the providers supported by the Enterprise Library wrapper class.
''' </summary>
''' <remarks>
''' This class currently supports only the following providers
''' i.e. all the providers supported by Enterprise Library -
''' January 2006 and installed in our systems.
''' Others can be added on later as required.
''' </remarks>
Public Enum enumProviderFactory
''' <summary>
''' System.Data.OleDb
''' </summary>
''' <remarks>Use for a OleDb connection.</remarks>
OleDb = 1
''' <summary>
''' System.Data.SqlClient
''' </summary>
''' <remarks>Use for a SQL Server connection.</remarks>
Sql = 2
''' <summary>
''' System.Data.Odbc
''' </summary>
''' <remarks>Use for an ODBC connection.</remarks>
Odbc = 3
''' <summary>
''' System.Data.OracleClient
''' </summary>
''' <remarks>Use for an Oracle connection.</remarks>
Oracle = 4
End Enum
''' <summary>
''' Instantiate a new Enterprise Library wrapper.
''' </summary>
''' <param name="connectionString">
''' Pass in the connection string at run time.
''' The connection string can be stored by the user in
''' Application / Session variables in an encrypted
''' format for security purposes. When calling this procedure pass
''' in the decrypted connection string.
''' </param>
''' <param name="Provider">
''' Using the public enum enumProviderFactory choose which
''' ProviderFactory you want to use.
''' </param>
''' <remarks>
''' User can store the password in appSettings in an
''' encrypted format or retrieve the same
''' from a database to format the connection string at run-time dynamically.
''' </remarks>
Public Sub New(ByVal connectionString As String, _
ByVal Provider As enumProviderFactory)
Select Case Provider
Case enumProviderFactory.OleDb
db = GetDatabase(connectionString, "System.Data.OleDb")
Case enumProviderFactory.Sql
db = GetDatabase(connectionString, "System.Data.SqlClient")
Case enumProviderFactory.Odbc
db = GetDatabase(connectionString, "System.Data.Odbc")
Case enumProviderFactory.Oracle
db = GetDatabase(connectionString, "System.Data.OracleClient")
End Select
conString = connectionString
dbProvider = Provider
End Sub
''' <summary>
''' To close the instance of this class.
''' </summary>
''' <remarks>
''' Always call the Close method after you are finished to free up all used resources.
''' </remarks>
Public Sub Close()
db = Nothing
dbCommand = Nothing
If Not dbread Is Nothing Then
dbread.Close()
End If
If Not dbConn Is Nothing Then
dbConn.Close()
End If
End Sub
''' <summary>
''' Gives access to all the other overloads in the
''' Enterprise Library not exposed by this class.
''' </summary>
''' <value>
''' Database
''' </value>
''' <returns>
''' Returns a database.
''' </returns>
''' <remarks>
''' This will enable the user to use all the other methods and
''' properties of the generic database which are not handled in this class
''' - this class handles only the most frequently used
''' methods to make it simpler for use.
''' </remarks>
Public ReadOnly Property Database() As Database
Get
Return db
End Get
End Property
''' <summary>
''' Retrieves the rows affected by a DoActionQuery.
''' </summary>
''' <value>Integer</value>
''' <returns>Returns an integer representing the rows affected
''' after DoActionQuery.</returns>
''' <remarks>
''' You can retrieve the rows affected after a DoActionQuery.
''' </remarks>
Public ReadOnly Property RowsAffected() As Integer
Get
Return dbRowsAffected
End Get
End Property
''' <summary>
''' Use to retrieve the id of the last row inserted after a DoActionQuery.
''' </summary>
''' <value>Integer</value>
''' <returns>Returns an integer representing the id of the
''' last row inserted after a DoActionQuery.</returns>
''' <remarks>
''' You can retrieve the id of the last row inserted after a DoActionQuery.
''' <param></param>
''' The Identity property returns the @@IDENTITY for OleDb,
''' the SCOPE_IDENTITY() for Sql and zero for the
''' other two providers, ODBC and Oracle.
''' <param></param>
''' For ODBC, setting this to zero as the ODBC connection string
''' may be pointing to a text file for example.
''' <param></param>
''' For Oracle, there is no such thing as scope identity -
''' have to use sequences, so returning zero for this too.
''' </remarks>
Public ReadOnly Property Identity() As Integer
Get
Return dbIdentity
End Get
End Property
''' <summary>
''' This is a private function of this class.
''' Provides the hook into the Enterprise Library.
''' </summary>
''' <param name="connectionString">The connection string to use
''' for connecting to the database.</param>
''' <param name="dbProviderFactoryString">Based on the provider selected
''' by the user the dbProviderFactoryString to use for the connection.</param>
''' <returns>A generic database</returns>
''' <remarks>
''' This is a private function of this class. Provides the hook
''' into the Enterprise Library.
''' </remarks>
Private Function GetDatabase(ByVal connectionString As String, _
ByVal dbProviderFactoryString As String) As Database
Return New GenericDatabase(connectionString, _
System.Data.Common.DbProviderFactories.GetFactory(dbProviderFactoryString))
End Function
''' <summary>
''' Pass in a sql string to get an IDataReader.
''' </summary>
''' <param name="sql">The sql string to use.</param>
''' <returns>An IDataReader</returns>
''' <remarks>
''' Depending on the provider factory chosen when the class was instantiated,
''' the returned data reader is converted to the appropriate type
''' - this is required for example to ensure that the HasRows method can be
''' used in OleDbDataReaders.
''' </remarks>
Public Function GetDataReader(ByVal sql As String) As IDataReader
dbCommand = db.GetSqlStringCommand(sql)
Select Case dbProvider
Case enumProviderFactory.OleDb
dbread = CType(db.ExecuteReader(dbCommand), OleDbDataReader)
Case enumProviderFactory.Sql
dbread = CType(db.ExecuteReader(dbCommand), SqlDataReader)
Case enumProviderFactory.Odbc
dbread = CType(db.ExecuteReader(dbCommand), OdbcDataReader)
Case enumProviderFactory.Oracle
dbread = CType(db.ExecuteReader(dbCommand), OracleDataReader)
End Select
Return dbread
End Function
''' <summary>
''' Pass in a sql string and tablename to get a dataset.
''' </summary>
''' <param name="sql">The sql string to use for getting the data.</param>
''' <param name="TableName">The tablename to assign for the table in the dataset.
''' </param>
''' <returns>A dataset</returns>
''' <remarks>Use when you need a new dataset; Use LoadDataSet
''' if you want to load a new table
''' in an existing dataset.
''' </remarks>
Public Function GetDataSet(ByVal sql As String, ByVal TableName As String) As DataSet
Dim ds As DataSet = New DataSet
ds = db.ExecuteDataSet(CommandType.Text, sql)
ds.Tables(0).TableName = TableName
Return ds
End Function
''' <summary>
''' Pass in a sql string, an existing dataset (ByRef)
''' and the name of the table to be added to the dataset.
''' </summary>
''' <param name="sql">The sql string to use for getting the data.</param>
''' <param name="ds">An existing database to which the data
''' is to be added as a table.</param>
''' <param name="TableName">The table name to use when adding
''' the data to the dataset.</param>
''' <remarks>
''' Use when you want to load a new table to an existing dataset.
''' Use GetDataSet when you need a new dataset.
''' </remarks>
Public Sub LoadDataSet(ByVal sql As String, ByRef ds As DataSet, _
ByVal TableName As String)
db.LoadDataSet(CommandType.Text, sql, ds, New String() {TableName})
End Sub
''' <summary>
''' Pass in a sql string and tablename to get a datatable.
''' </summary>
''' <param name="sql">The sql string to use for getting the data.</param>
''' <param name="TableName">The tablename to assign for the returned datatable.
''' </param>
''' <returns>A DataTable</returns>
''' <remarks>Use to get a DataTable. See all GetTableWithZero method.</remarks>
Public Function GetDataTable(ByVal sql As String, _
ByVal TableName As String) As DataTable
Dim ds As DataSet = New DataSet
ds = db.ExecuteDataSet(CommandType.Text, sql)
ds.Tables(0).TableName = TableName
Return ds.Tables(TableName)
End Function
''' <summary>
''' Use to get an IDbDataAdapter.
''' </summary>
''' <returns>Returns an IDbDataAdapter</returns>
''' <remarks>When you instantiated the class
''' you have already passed in the connection string and
''' which provider to use - so you don't need to pass this info. again!
''' </remarks>
Public Function GetDataAdapter() As IDbDataAdapter
Return db.GetDataAdapter()
End Function
''' <summary>
''' Pass in sql string containing an action query.
''' </summary>
''' <param name="sql">Sql string containing action query.</param>
''' <returns>Returns the word "Success" if the action query completed successfully;
''' If the action query did not complete successfully
''' the return string will contain the error message.
''' </returns>
''' <remarks>
''' User to check return value and handle any errors as appropriate.
''' <param>
''' A template as follows is suggested for handling errors within a Try Catch block:
''' </param>
''' <param>
''' <code>
''' retval = ELW.DoActionQuery(sql)
''' If Not retval = "Success" Then
''' Err.Raise(513, , retval)
''' Exit Try
''' End If
'''
''' </code>
''' </param>
''' Use the RowsAffected property to retrieve the number of rows
''' affected after a DoActionQuery.
''' <param></param>
''' Use the Identity property to retrieve the id of the last row
''' inserted after an insert query is executed.
''' <param></param>
''' The Identity property returns the @@IDENTITY for OleDb,
''' the SCOPE_IDENTITY() for Sql and zero for the
''' other two providers, ODBC and Oracle.
''' <param></param>
''' For ODBC, setting this to zero as the ODBC connection string
''' may be pointing to a text file for example.
''' <param></param>
''' For Oracle, there is no such thing as scope identity -
''' have to use sequences, so returning zero for this too.
''' </remarks>
Public Function DoActionQuery(ByVal sql As String) As String
Dim retval As String
Dim cmdGetIdentity As DbCommand
Select Case dbProvider
Case enumProviderFactory.OleDb
dbConn = New OleDbConnection(conString)
dbConn.Open()
dbCommand = New OleDbCommand(sql, dbConn)
cmdGetIdentity = New OleDbCommand()
cmdGetIdentity.CommandText = "SELECT @@IDENTITY"
cmdGetIdentity.Connection = dbConn
Case enumProviderFactory.Sql
dbConn = New SqlConnection(conString)
dbConn.Open()
dbCommand = New SqlCommand(sql, dbConn)
cmdGetIdentity = New SqlCommand()
cmdGetIdentity.CommandText = _
"SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]"
cmdGetIdentity.Connection = dbConn
Case enumProviderFactory.Odbc
dbConn = New OdbcConnection(conString)
dbConn.Open()
dbCommand = New OdbcCommand(sql, dbConn)
Case enumProviderFactory.Oracle
dbConn = New OracleConnection(conString)
dbConn.Open()
dbCommand = New OracleCommand(sql, dbConn)
End Select
dbRowsAffected = 0
Try
dbRowsAffected = dbCommand.ExecuteNonQuery()
Select Case dbProvider
Case enumProviderFactory.OleDb, enumProviderFactory.Sql
dbIdentity = cmdGetIdentity.ExecuteScalar().ToString()
Case enumProviderFactory.Odbc, enumProviderFactory.Oracle
dbIdentity = 0
' For ODBC setting this to zero as the ODBC connection string may be
' pointing to a text file for example
' For Oracle there is not such thing as scope identity -
' have to use sequences
' so returning o for this too
End Select
retval = "Success" ' you can check for this in the string and do whatever
Catch exc As Exception
retval = "Error: " & exc.Message & "<br><br>" & _
exc.InnerException.Message & "."
Finally
dbCommand = Nothing
cmdGetIdentity = Nothing
dbConn.Close()
End Try
Return retval
End Function
''' <summary>
''' Pass in the SQLString, ValueField (first column) and TextField
''' (second column) to get a DataTable with an additional zero record
''' containing "FirstDummyRecord" for the first column and an empty string
''' for the second column.
''' </summary>
''' <param name="SQLString">The sql string to use for getting the data.</param>
''' <param name="ValueField">The name of the value field
''' which will be assigned to the first column.</param>
''' <param name="TextField">The name of the text field which will be
''' assigned to the second column.</param>
''' <returns>A DataTable</returns>
''' <remarks>
''' This function returns a data table including a first dummy record which is empty.
''' This data table can then be bound to a dropdown list to enable
''' the user to unselect by choosing the first empty record;
''' Then in code we can check for "FirstDummyRecord"
''' and act appropriately. See all GetDataTable method.
''' </remarks>
Public Function GetTableWithZero(ByVal SQLString As String, _
ByVal ValueField As String, ByVal TextField As String) As DataTable
Dim dtTable As DataTable = New DataTable
Dim dr As DataRow
Dim dcDescription As New DataColumn("description")
Dim dcID As New DataColumn("id")
dtTable.Columns.Add(dcID)
dtTable.Columns.Add(dcDescription)
' Add zero record
dr = dtTable.NewRow()
dr(0) = "FirstDummyRecord"
dr(1) = ""
dtTable.Rows.Add(dr)
dbread = db.ExecuteReader(CommandType.Text, SQLString)
While (dbread.Read())
dr = dtTable.NewRow()
dr(0) = dbread(ValueField)
dr(1) = dbread(TextField)
dtTable.Rows.Add(dr)
End While
dbread.Close()
Return dtTable
End Function
End Class
The following helper functions are used to encrypt / decrypt appSettings
in web.config as well as application variables which are used to assemble and store connection strings in memory during run-time dynamically:
Imports System.Configuration
Imports system.Web.Configuration
Imports Microsoft.Practices.EnterpriseLibrary.Security.Cryptography
Module Common
Private Const symmProvider As String = "SelfServiceCryptoProvider"
Public Sub ProtectSection(ByVal sectionName As String, _
ByVal provider As String, ByVal VirtualPath As String)
Dim config As Configuration = _
WebConfigurationManager.OpenWebConfiguration(VirtualPath)
Dim section As ConfigurationSection = config.GetSection(sectionName)
If ((section.SectionInformation.IsProtected = False) AndAlso _
(section.ElementInformation.IsLocked = False)) Then
' Protect (encrypt) the section.
section.SectionInformation.ProtectSection(provider)
' Save the encrypted section.
section.SectionInformation.ForceSave = True
config.Save(ConfigurationSaveMode.Full)
'config.Save()
End If
End Sub
Public Sub UnProtectSection(ByVal sectionName As String, _
ByVal VirtualPath As String)
Dim config As Configuration = _
WebConfigurationManager.OpenWebConfiguration(VirtualPath)
Dim section As ConfigurationSection = config.GetSection(sectionName)
'If ((section.SectionInformation.IsProtected = False) AndAlso _
' (section.ElementInformation.IsLocked = False)) Then
' UnProtect (decrypt) the section.
section.SectionInformation.UnprotectSection()
' Save the decrypted section.
section.SectionInformation.ForceSave = True
config.Save(ConfigurationSaveMode.Full)
'config.Save()
'End If
End Sub
Public Function EncryptValue(ByVal UnEncryptedValue As String) As String
Dim RetVal As String
If (Not (UnEncryptedValue Is Nothing) AndAlso _
UnEncryptedValue.Trim().Length > 0) Then
RetVal = Cryptographer.EncryptSymmetric(symmProvider, UnEncryptedValue & "")
End If
Return RetVal
End Function
Public Function DecryptValue(ByVal EncryptedValue As String) As String
Dim RetVal As String
If (Not (EncryptedValue Is Nothing) _
AndAlso EncryptedValue.Trim().Length > 0) Then
RetVal = Cryptographer.DecryptSymmetric(symmProvider, EncryptedValue & "")
End If
Return RetVal
End Function
End Module
I generate my connection strings when the application starts in Global.asax as follows - the password is retrieved from web.config. Note that the connection string is stored in application variables in an encrypted form and decrypted when instantiating the EntLibWrapper
class. Also note that the appSettings
in web.config is encrypted or decrypted based on a parameter retrieved from a database - this enables the developer to add new appSettings
later after decrypting the section:
Const UserId As String = "YourUserNameGoesHere"
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Fires when the application is started
Dim Password As String = ConfigurationManager.AppSettings("SYSPWD")
Application("News") = EncryptValue_
("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
Server.MapPath("../fpdb/News.mdb") & ";Jet OLEDB:System Database=" _
& Server.MapPath("../fpdb/SelfService.mdw") & ";User ID=" & _
UserId & ";Password=" & Password & ";")
Dim ELW As New EntLibWrapper(DecryptValue(Application("News")), _
EntLibWrapper.enumProviderFactory.OleDb)
Dim dbread As OleDbDataReader
Dim sql As String
Dim EncryptAppSettings As Boolean
' GET PARAMETER - EncryptAppSettings
sql = "SELECT tblParameters.*"
sql = sql & " FROM [tblParameters]"
sql = sql & " WHERE (((tblParameters.RecordId)='EncryptAppSettings'));"
dbread = ELW.GetDataReader(sql)
If dbread.HasRows Then
Do While dbread.Read()
EncryptAppSettings = dbread.Item("MyBoolean")
Loop
Else
EncryptAppSettings = True
End If
dbread.Close()
ELW.Close()
If EncryptAppSettings = True Then
'Call ProtectSection("appSettings", _
"DataProtectionConfigurationProvider", "/timetable/Documentation")
'Call ProtectSection("appSettings", _
"RSAProtectedConfigurationProvider", "/timetable/Documentation")
Call ProtectSection("appSettings", _
"SelfServiceConfigurationProvider", "/timetable/Documentation")
Else
Call UnProtectSection("appSettings", "/timetable/Documentation")
End If
End Sub
Then where I need to get the data, the code is as follows for getting a data reader and binding it to a dropdownlist
:
Dim ELW As New EntLibWrapper(DecryptValue(Application("News")), _
EntLibWrapper.enumProviderFactory.OleDb)
Dim dbread As OleDbDataReader
Dim sql As String
' LOAD ddCountry
sql = "SELECT qryNewsCountry.*"
sql = sql & " FROM qryNewsCountry;"
dbread = ELW.GetDataReader(sql)
ddCountry.DataSource = dbread
ddCountry.DataTextField = "CountryName"
ddCountry.DataValueField = "Country"
If Session("SelectedCountryIndex") Is Nothing Then
ddCountry.SelectedIndex = 0
Else
ddCountry.SelectedIndex = Session("SelectedCountryIndex")
End If
ddCountry.DataBind()
dbread.Close()
' Do other things with the ELW object and then finally close it
ELW.Close()
For the encryption / decryption to work, we need web.config entries. The sample web.config you have downloaded contains entries which have been created using the Enterprise Library Configuration tool.
The following links are useful to fully understand issues regarding encryption / decryption. This will also help you to create your encryption keys:
- How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA
- How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI
- Encrypting Connection Strings in ASP.NET 2.0
Please note that the EntLibWrapper
class can be used even if you are not bothered about encryption / decryption. The class does not force you to encrypt appSettings
in web.config or the connection strings stored in application variables. You are also not forced to store the connections strings in application variables - you can also retrieve it from a database if that suits your requirement better.
The EntLibWrapper
class has the following methods / properties / enums / references:
Methods
Close
LoadDataSet
New
DoActionQuery
GetDataAdapter
GetDatabase
GetDataReader
GetDataSet
GetDataTable
GetTableWithZero
Properties
Database
- This enables the user to get access to the other methods and properties in theGenericDatabase
inEnterprise Library
Identity
- After calling theDoActionQuery
method to insert a record, the user can retrieve the id of the last record inserted using this property.RowsAffected
- After calling theDoActionQuery
method, the user can retrieve the number of rows affected from this property.
Enum
enumProviderFactory
References
Microsoft.Practices.EnterpriseLibrary.Common
Microsoft.Practices.EnterpriseLibrary.Data
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography
System.Configuration
System.Data.Common
System.Data.Odbc
System.Data.OleDb
System.Data.OracleClient
System.Data.SqlClient
System.Web.Configuration
The downloaded EntLibWrapper.CHM file should give you more details of the EntLibWrapper
class. When you use the class in your project, you will notice that it integrates well with Intellisense and the Object Explorer in Visual Studio. I was also able to generate the CHM file using nDocs easily. In case you are wondering how this was achieved, you can have a look at the following links:
Thanks to those who have given me feedback based on which I was able to improve the class further. Hope this enterprise library wrapper class is of use to others too.
Cheers
Raja Lakshman
History
- 11th June, 2009
- Initial post
- 16th June, 2009
RowsAffected
andIdentity
properties added- 26th June, 2009
- Added
LoadDataSet
method - Added
Encryption
/Decryption
functions - Compiled help file added using XML comments in code.