using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using BLToolkit.Configuration;
namespace BLToolkit.Data
{
using DataProvider;
using Properties;
public partial class DbManager
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class
/// and opens a database connection.
/// </summary>
/// <remarks>
/// <para>
/// This constructor uses a configuration, which has been used first in your application.
/// If there has been no connection used before, an empty string is applied as a default configuration.
/// </para>
/// <para>
/// See the <see cref="ConfigurationString"/> property
/// for an explanation and use of the default configuration.
/// </para>
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="ctor"]/*' />
/// <seealso cref="AddConnectionString(string)"/>
/// <returns>An instance of the database manager class.</returns>
[DebuggerStepThrough]
public DbManager() : this(DefaultConfiguration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class
/// and opens a database connection for the provided configuration.
/// </summary>
/// <remarks>
/// See the <see cref="ConfigurationString"/> property
/// for an explanation and use of the configuration string.
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="ctor(string)"]/*' />
/// <param name="configurationString">Configuration string.</param>
/// <returns>An instance of the <see cref="DbManager"/> class.</returns>
[DebuggerStepThrough]
public DbManager(string configurationString)
: this(
GetDataProvider (configurationString),
GetConnectionString(configurationString))
{
_configurationString = configurationString;
}
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class
/// and opens a database connection for the provided configuration.
/// </summary>
/// <remarks>
/// See the <see cref="ConfigurationString"/> property
/// for an explanation and use of the configuration string.
/// </remarks>
/// <param name="configuration">Configuration string not containing provider name.</param>
/// <param name="providerName">Provider configuration name.</param>
/// <returns>An instance of the <see cref="DbManager"/> class.</returns>
[DebuggerStepThrough]
public DbManager(string providerName, string configuration)
: this(
GetDataProvider (providerName + ProviderNameDivider + configuration),
GetConnectionString(providerName + ProviderNameDivider + configuration))
{
_configurationString = providerName + ProviderNameDivider + configuration;
}
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class for the provided connection.
/// </summary>
/// <remarks>
/// This constructor tries to open the connection if the connection state equals
/// <see cref="System.Data.ConnectionState">ConnectionState.Closed</see>.
/// In this case the <see cref="IDbConnection.ConnectionString"/> property of the connection
/// must be set before colling the constructor.
/// Otherwise, it neither opens nor closes the connection.
/// </remarks>
/// <exception cref="DataException">
/// Type of the connection could not be recognized.
/// </exception>
/// <include file="Examples.xml" path='examples/db[@name="ctor(IDbConnection)"]/*' />
/// <param name="connection">An instance of the <see cref="IDbConnection"/> class.</param>
/// <returns>An instance of the <see cref="DbManager"/> class.</returns>
[DebuggerStepThrough]
public DbManager(IDbConnection connection)
: this(GetDataProvider(connection), connection)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class for the provided transaction.
/// </summary>
/// <include file="Examples.xml" path='examples/db[@name="ctor(IDbTransaction)"]/*' />
/// <param name="transaction"></param>
[DebuggerStepThrough]
public DbManager(IDbTransaction transaction)
: this(GetDataProvider(transaction.Connection), transaction)
{
}
/*
/// <summary>
/// Initializes a new instance of the <see cref="DbManager"/> class
/// and opens a database connection for the provided configuration and database connection.
/// </summary>
/// <remarks>
/// <para>
/// This constructor opens the connection only if the connection state equals
/// <see cref="System.Data.ConnectionState">ConnectionState.Closed</see>.
/// Otherwise, it neither opens nor closes the connection.
/// </para>
/// <para>
/// See the <see cref="ConfigurationString"/> property
/// for an explanation and use of the configuration string.
/// </para>
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="ctor(IDbConnection,string)"]/*' />
/// <param name="connection">An instance of the <see cref="IDbConnection"/>.</param>
/// <param name="configurationString">The configuration string.</param>
/// <returns>An instance of the <see cref="DbManager"/> class.</returns>
[DebuggerStepThrough]
public DbManager(
IDbConnection connection,
string configurationString)
{
if (connection == null)
{
Init(configurationString);
if (configurationString != null)
OpenConnection(configurationString);
}
else
{
Init(connection);
_configurationString = configurationString;
_connection.ConnectionString = GetConnectionString(configurationString);
if (_connection.State == ConnectionState.Closed)
OpenConnection();
}
}
*/
#endregion
#region Public Properties
private string _configurationString;
/// <summary>
/// Gets the string used to open a database.
/// </summary>
/// <value>
/// A string containing configuration settings.
/// </value>
/// <remarks>
/// <para>
/// An actual database connection string is read from the <i>appSettings</i> section
/// of application configuration file (App.config, Web.config, or Machine.config)
/// according to the follow rule:
/// </para>
/// <code>
/// <appSettings>
/// <add
/// key = "ConnectionString.<b>configurationString</b>"
/// va<i></i>lue = "Server=(local);Database=Northwind;Integrated Security=SSPI" />
/// </appSettings>
/// </code>
/// <para>
/// If the configuration string is empty, the following rule is applied:
/// </para>
/// <code>
/// <appSettings>
/// <add
/// key = "ConnectionString"
/// va<i></i>lue = "Server=(local);Database=Northwind;Integrated Security=SSPI" />
/// </appSettings>
/// </code>
/// <para>
/// If you don't want to use a configuration file, you can add a database connection string
/// using the <see cref="AddConnectionString(string)"/> method.
/// </para>
/// <para>
/// The configuration string may have a prefix used to define a data provider. The following table
/// contains prefixes for all supported data providers:
/// <list type="table">
/// <listheader><term>Prefix</term><description>Provider</description></listheader>
/// <item><term>Sql</term><description>Data Provider for SQL Server</description></item>
/// <item><term>OleDb</term><description>Data Provider for OLE DB</description></item>
/// <item><term>Odbc</term><description>Data Provider for ODBC</description></item>
/// <item><term>Oracle</term><description>Data Provider for Oracle</description></item>
/// </list>
/// </para>
/// </remarks>
/// <seealso cref="AddConnectionString(string)"/>
public string ConfigurationString
{
[DebuggerStepThrough]
get { return _configurationString; }
}
#endregion
#region Config Overrides
protected virtual void InitDataProvider(IDbConnection connection)
{
DataProvider = GetDataProvider(connection);
}
protected virtual IDbConnection CloneConnection()
{
if (Connection is ICloneable || ConfigurationString == null)
return _dataProvider.CloneConnection(_connection);
IDbConnection con = DataProvider.CreateConnectionObject();
con.ConnectionString = GetConnectionString(ConfigurationString);
return con;
}
protected virtual string GetConnectionHash()
{
return ConfigurationString ?? Connection.ConnectionString.GetHashCode().ToString();
}
#endregion
#region Protected Static Members
static DbManager()
{
AddDataProvider(new Sql2008DataProvider());
AddDataProvider(new SqlDataProvider());
AddDataProvider(new AccessDataProvider());
AddDataProvider(new OleDbDataProvider());
AddDataProvider(new OdbcDataProvider());
BLToolkitSection section = BLToolkitSection.Instance;
if (section != null)
{
_defaultConfiguration = section.DefaultConfiguration;
foreach (DataProviderElement provider in section.DataProviders)
{
Type dataProviderType = Type.GetType(provider.TypeName, true);
DataProviderBase providerInstance = (DataProviderBase)Activator.CreateInstance(dataProviderType);
if (!string.IsNullOrEmpty(provider.Name))
providerInstance.UniqueName = provider.Name;
providerInstance.Configure(provider.Attributes);
AddDataProvider(providerInstance);
if (!provider.Default)
continue;
if (_defaultDataProviderName != null)
{
throw new ConfigurationErrorsException(string.Format(
Resources.DbManager_MoreThenOneDefaultProvider, _defaultDataProviderName, providerInstance.UniqueName),
provider.ElementInformation.Source, provider.ElementInformation.LineNumber);
}
_defaultDataProviderName = providerInstance.UniqueName;
}
}
string dataProviders = ConfigurationManager.AppSettings.Get("BLToolkit.DataProviders");
if (dataProviders != null)
{
Debug.WriteLineIf(TraceSwitch.TraceWarning, "Using appSettings\\BLToolkit.DataProviders is obsolete. Consider using bltoolkit configuration section instead.", TraceSwitch.DisplayName);
foreach (string dataProviderTypeName in dataProviders.Split(';'))
AddDataProvider(Type.GetType(dataProviderTypeName, true));
}
if (string.IsNullOrEmpty(_defaultConfiguration))
_defaultConfiguration = ConfigurationManager.AppSettings.Get("BLToolkit.DefaultConfiguration");
if (string.IsNullOrEmpty(_defaultDataProviderName))
_defaultDataProviderName = SqlDataProviderBase.NameString;
}
private static string _firstConfiguration;
private static DataProviderBase _firstProvider;
private static readonly Hashtable _configurationList = Hashtable.Synchronized(new Hashtable());
private static readonly Hashtable _anyProviderConfigurationList = Hashtable.Synchronized(new Hashtable());
private static DataProviderBase GetDataProvider(IDbConnection connection)
{
if (connection == null) throw new ArgumentNullException("connection");
DataProviderBase dp = _dataProviderTypeList[connection.GetType()];
if (dp == null)
throw new DataException(string.Format(
Resources.DbManager_UnknownConnectionType, connection.GetType().FullName));
return dp;
}
public static DataProviderBase GetDataProvider(string configurationString)
{
if (configurationString == null) throw new ArgumentNullException("configurationString");
if (configurationString.StartsWith(AnyProvider))
return FindFirstSuitableProvider(configurationString);
if (configurationString == _firstConfiguration)
return _firstProvider;
DataProviderBase dp = (DataProviderBase)_configurationList[configurationString];
if (dp == null)
{
ConnectionStringSettings css = ConfigurationManager.ConnectionStrings[configurationString];
if (css != null && !string.IsNullOrEmpty(css.ProviderName))
{
// This hack should be redone.
//
string provider = css.ProviderName == "System.Data.SqlClient" ?
configurationString.IndexOf("2008") >= 0 ?
"MSSQL2008" :
css.ProviderName :
css.ProviderName;
dp = _dataProviderNameList[provider];
}
else
{
// configurationString can be:
// '' : default provider, default configuration;
// '.' : default provider, default configuration;
// 'foo.bar' : 'foo' provider, 'bar' configuration;
// 'foo.' : 'foo' provider, default configuration;
// 'foo' : default provider, 'foo' configuration or
// foo provider, default configuration;
// '.foo' : default provider, 'foo' configuration;
// '.foo.bar': default provider, 'foo.bar' configuration;
//
// Default provider is SqlDataProvider
//
string cs = configurationString.ToUpper();
string key = _defaultDataProviderName;
if (cs.Length > 0)
{
cs += ProviderNameDivider;
foreach (string k in _dataProviderNameList.Keys)
{
if (cs.StartsWith(k + ProviderNameDivider))
{
key = k;
break;
}
}
}
dp = _dataProviderNameList[key];
}
if (dp == null)
throw new DataException(string.Format(
Resources.DbManager_UnknownDataProvider, configurationString));
_configurationList[configurationString] = dp;
}
if (_firstConfiguration == null)
{
lock (_configurationList.SyncRoot)
{
if (_firstConfiguration == null)
{
_firstConfiguration = configurationString;
_firstProvider = dp;
}
}
}
return dp;
}
private static bool IsMatchedConfigurationString(string configurationString, string csWithoutProvider)
{
int dividerPos;
return
!configurationString.StartsWith(AnyProvider) &&
(dividerPos = configurationString.IndexOf(ProviderNameDivider)) >= 0 &&
0 == StringComparer.OrdinalIgnoreCase.Compare
(configurationString.Substring(dividerPos + ProviderNameDivider.Length), csWithoutProvider);
}
private static DataProviderBase FindFirstSuitableProvider(string configurationString)
{
string cs = (string)_anyProviderConfigurationList[configurationString];
bool searchRequired = (cs == null);
if (searchRequired)
{
string csWithoutProvider = configurationString.Substring(AnyProvider.Length);
if (configurationString.Length == 0) throw new ArgumentNullException("configurationString");
foreach (string str in _connectionStringList.Keys)
{
if (IsMatchedConfigurationString(str, csWithoutProvider))
{
cs = str;
break;
}
}
if (cs == null)
{
foreach (ConnectionStringSettings css in ConfigurationManager.ConnectionStrings)
{
if (IsMatchedConfigurationString(css.Name, csWithoutProvider))
{
cs = css.Name;
break;
}
}
}
if (cs == null)
{
foreach (string name in ConfigurationManager.AppSettings.AllKeys)
{
if (name.StartsWith("ConnectionString" + ProviderNameDivider))
{
string str = name.Substring(name.IndexOf(ProviderNameDivider) + ProviderNameDivider.Length);
if (IsMatchedConfigurationString(str, csWithoutProvider))
{
cs = str;
break;
}
}
}
}
if (cs == null)
cs = csWithoutProvider;
}
DataProviderBase dp = GetDataProvider(cs);
if (searchRequired)
_anyProviderConfigurationList[configurationString] = cs;
return dp;
}
private static readonly Dictionary<string,string> _connectionStringList = new Dictionary<string,string>(4);
public static string GetConnectionString(string configurationString)
{
// Use default configuration.
//
if (configurationString == null)
configurationString = DefaultConfiguration;
if (_anyProviderConfigurationList.Contains(configurationString))
configurationString = (string)_anyProviderConfigurationList[configurationString];
string str;
// Check cached strings first.
//
if (!_connectionStringList.TryGetValue(configurationString, out str))
{
lock (_dataProviderListLock)
{
// Connection string is not in the cache.
//
string key = string.Format("ConnectionString{0}{1}",
configurationString.Length == 0? String.Empty: ProviderNameDivider, configurationString);
ConnectionStringSettings css = ConfigurationManager.ConnectionStrings[configurationString];
str = css != null? css.ConnectionString: ConfigurationManager.AppSettings.Get(key);
if (string.IsNullOrEmpty(str))
{
throw new DataException(string.Format(
Resources.DbManager_UnknownConfiguration, key));
}
// Store the result in cache.
//
_connectionStringList[configurationString] = str;
}
}
return str;
}
/*
private void OpenConnection(string configurationString)
{
// If connection is already opened, we close it and open again.
//
if (_connection != null)
{
Dispose();
GC.ReRegisterForFinalize(this);
}
// Store the configuration string.
//
_configurationString = configurationString;
// Create and open the connection object.
//
_connection = _dataProvider.CreateConnectionObject();
_connection.ConnectionString = GetConnectionString(ConfigurationString);
OpenConnection();
}
*/
#endregion
#region AddDataProvider
static readonly Dictionary<string, DataProviderBase> _dataProviderNameList = new Dictionary<string, DataProviderBase>(8, StringComparer.OrdinalIgnoreCase);
static readonly Dictionary<Type, DataProviderBase> _dataProviderTypeList = new Dictionary<Type, DataProviderBase>(4);
static readonly object _dataProviderListLock = new object();
/// <summary>
/// Adds a new data provider.
/// </summary>
/// <remarks>
/// The method can be used to register a new data provider for further use.
/// </remarks>
/// <include file="Examples1.xml" path='examples/db[@name="AddDataProvider(DataProvider.IDataProvider)"]/*' />
/// <seealso cref="AddConnectionString(string)"/>
/// <seealso cref="BLToolkit.Data.DataProvider.DataProviderBase.Name"/>
/// <param name="dataProvider">An instance of the <see cref="BLToolkit.Data.DataProvider.DataProviderBase"/> interface.</param>
public static void AddDataProvider(DataProviderBase dataProvider)
{
if (null == dataProvider)
throw new ArgumentNullException("dataProvider");
if (string.IsNullOrEmpty(dataProvider.UniqueName))
throw new ArgumentException(Resources.DbManager_InvalidDataProviderName, "dataProvider");
if (string.IsNullOrEmpty(dataProvider.ProviderName))
throw new ArgumentException(Resources.DbManager_InvalidDataProviderProviderName, "dataProvider");
if (dataProvider.ConnectionType == null || !typeof(IDbConnection).IsAssignableFrom(dataProvider.ConnectionType))
throw new ArgumentException(Resources.DbManager_InvalidDataProviderConnectionType, "dataProvider");
lock (_dataProviderListLock)
{
_dataProviderNameList[dataProvider.UniqueName.ToUpper()] = dataProvider;
_dataProviderNameList[dataProvider.ProviderName] = dataProvider;
_dataProviderTypeList[dataProvider.ConnectionType] = dataProvider;
}
}
/// <summary>
/// Adds a new data provider witch a specified name.
/// </summary>
/// <remarks>
/// The method can be used to register a new data provider for further use.
/// </remarks>
/// <include file="Examples1.xml" path='examples/db[@name="AddDataProvider(DataProvider.IDataProvider)"]/*' />
/// <seealso cref="AddConnectionString(string)"/>
/// <seealso cref="BLToolkit.Data.DataProvider.DataProviderBase.Name"/>
/// <param name="providerName">The data provider name.</param>
/// <param name="dataProvider">An instance of the <see cref="BLToolkit.Data.DataProvider.DataProviderBase"/> interface.</param>
public static void AddDataProvider(string providerName, DataProviderBase dataProvider)
{
if (dataProvider == null)
throw new ArgumentNullException("dataProvider");
if (string.IsNullOrEmpty(providerName))
throw new ArgumentException(Resources.DbManager_InvalidDataProviderName, "providerName");
dataProvider.UniqueName = providerName;
AddDataProvider(dataProvider);
}
/// <summary>
/// Adds a new data provider.
/// </summary>
/// <remarks>
/// The method can be used to register a new data provider for further use.
/// </remarks>
/// <seealso cref="AddConnectionString(string)"/>
/// <seealso cref="BLToolkit.Data.DataProvider.DataProviderBase.Name"/>
/// <param name="dataProviderType">A data provider type.</param>
public static void AddDataProvider(Type dataProviderType)
{
AddDataProvider((DataProviderBase)Activator.CreateInstance(dataProviderType));
}
/// <summary>
/// Adds a new data provider witch a specified name.
/// </summary>
/// <remarks>
/// The method can be used to register a new data provider for further use.
/// </remarks>
/// <seealso cref="AddConnectionString(string)"/>
/// <seealso cref="BLToolkit.Data.DataProvider.DataProviderBase.Name"/>
/// <param name="providerName">The data provider name.</param>
/// <param name="dataProviderType">A data provider type.</param>
public static void AddDataProvider(string providerName, Type dataProviderType)
{
AddDataProvider(providerName, (DataProviderBase)Activator.CreateInstance(dataProviderType));
}
#endregion
#region AddConnectionString
/// <summary>
/// Adds a new connection string or replaces existing one.
/// </summary>
/// <remarks>
/// Use this method when you use only one configuration and
/// you don't want to use a configuration file.
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="AddConnectionString(string)"]/*' />
/// <param name="connectionString">A valid database connection string.</param>
public static void AddConnectionString(string connectionString)
{
AddConnectionString(string.Empty, connectionString);
}
/// <summary>
/// Adds a new connection string or replaces existing one.
/// </summary>
/// <remarks>
/// Use this method when you use multiple configurations and
/// you don't want to use a configuration file.
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="AddConnectionString(string,string)"]/*' />
/// <param name="configurationString">The configuration string.</param>
/// <param name="connectionString">A valid database connection string.</param>
public static void AddConnectionString(string configurationString, string connectionString)
{
_connectionStringList[configurationString] = connectionString;
}
/// <summary>
/// Adds a new connection string or replaces existing one.
/// </summary>
/// <remarks>
/// Use this method when you use multiple configurations and
/// you don't want to use a configuration file.
/// </remarks>
/// <include file="Examples.xml" path='examples/db[@name="AddConnectionString(string,string)"]/*' />
/// <param name="providerName">The data provider name.</param>
/// <param name="configurationString">The configuration string.</param>
/// <param name="connectionString">A valid database connection string.</param>
public static void AddConnectionString(
string providerName, string configurationString, string connectionString)
{
AddConnectionString(providerName + ProviderNameDivider + configurationString, connectionString);
}
#endregion
#region Public Static Properties
public const string ProviderNameDivider = ".";
public const string AnyProvider = "*" + ProviderNameDivider;
private static readonly string _defaultDataProviderName;
private static string _defaultConfiguration;
/// <summary>
/// Gets and sets the default configuration string.
/// </summary>
/// <remarks>
/// See the <see cref="ConfigurationString"/> property
/// for an explanation and use of the default configuration.
/// </remarks>
/// <value>
/// A string containing default configuration settings.
/// </value>
/// <seealso cref="ConfigurationString"/>
public static string DefaultConfiguration
{
get
{
if (_defaultConfiguration == null)
{
// Grab first registered configuration.
//
foreach (KeyValuePair<string, string> de in _connectionStringList)
{
_defaultConfiguration = de.Key;
break;
}
if (_defaultConfiguration == null)
{
_defaultConfiguration = string.Empty;
foreach (ConnectionStringSettings css in ConfigurationManager.ConnectionStrings)
{
if (css.ElementInformation.Source != null &&
!css.ElementInformation.Source.EndsWith("machine.config", StringComparison.OrdinalIgnoreCase))
{
_defaultConfiguration = css.Name;
break;
}
}
}
}
return _defaultConfiguration;
}
set { _defaultConfiguration = value; }
}
#endregion
}
} |