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 /// /// Initializes a new instance of the class /// and opens a database connection. /// /// /// /// 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. /// /// /// See the property /// for an explanation and use of the default configuration. /// /// /// /// /// An instance of the database manager class. [DebuggerStepThrough] public DbManager() : this(DefaultConfiguration) { } /// /// Initializes a new instance of the class /// and opens a database connection for the provided configuration. /// /// /// See the property /// for an explanation and use of the configuration string. /// /// /// Configuration string. /// An instance of the class. [DebuggerStepThrough] public DbManager(string configurationString) : this( GetDataProvider (configurationString), GetConnectionString(configurationString)) { _configurationString = configurationString; } /// /// Initializes a new instance of the class /// and opens a database connection for the provided configuration. /// /// /// See the property /// for an explanation and use of the configuration string. /// /// Configuration string not containing provider name. /// Provider configuration name. /// An instance of the class. [DebuggerStepThrough] public DbManager(string providerName, string configuration) : this( GetDataProvider (providerName + ProviderNameDivider + configuration), GetConnectionString(providerName + ProviderNameDivider + configuration)) { _configurationString = providerName + ProviderNameDivider + configuration; } /// /// Initializes a new instance of the class for the provided connection. /// /// /// This constructor tries to open the connection if the connection state equals /// ConnectionState.Closed. /// In this case the property of the connection /// must be set before colling the constructor. /// Otherwise, it neither opens nor closes the connection. /// /// /// Type of the connection could not be recognized. /// /// /// An instance of the class. /// An instance of the class. [DebuggerStepThrough] public DbManager(IDbConnection connection) : this(GetDataProvider(connection), connection) { } /// /// Initializes a new instance of the class for the provided transaction. /// /// /// [DebuggerStepThrough] public DbManager(IDbTransaction transaction) : this(GetDataProvider(transaction.Connection), transaction) { } /* /// /// Initializes a new instance of the class /// and opens a database connection for the provided configuration and database connection. /// /// /// /// This constructor opens the connection only if the connection state equals /// ConnectionState.Closed. /// Otherwise, it neither opens nor closes the connection. /// /// /// See the property /// for an explanation and use of the configuration string. /// /// /// /// An instance of the . /// The configuration string. /// An instance of the class. [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; /// /// Gets the string used to open a database. /// /// /// A string containing configuration settings. /// /// /// /// An actual database connection string is read from the appSettings section /// of application configuration file (App.config, Web.config, or Machine.config) /// according to the follow rule: /// /// /// <appSettings> /// <add /// key = "ConnectionString.configurationString" /// value = "Server=(local);Database=Northwind;Integrated Security=SSPI" /> /// </appSettings> /// /// /// If the configuration string is empty, the following rule is applied: /// /// /// <appSettings> /// <add /// key = "ConnectionString" /// value = "Server=(local);Database=Northwind;Integrated Security=SSPI" /> /// </appSettings> /// /// /// If you don't want to use a configuration file, you can add a database connection string /// using the method. /// /// /// The configuration string may have a prefix used to define a data provider. The following table /// contains prefixes for all supported data providers: /// /// PrefixProvider /// SqlData Provider for SQL Server /// OleDbData Provider for OLE DB /// OdbcData Provider for ODBC /// OracleData Provider for Oracle /// /// /// /// 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 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 = SqlDataProvider.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)) { dp = _dataProviderNameList[css.ProviderName]; } 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 _connectionStringList = new Dictionary(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 private static readonly Dictionary _dataProviderNameList = new Dictionary(8, StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _dataProviderTypeList = new Dictionary(4); private static readonly object _dataProviderListLock = new object(); /// /// Adds a new data provider. /// /// /// The method can be used to register a new data provider for further use. /// /// /// /// /// An instance of the interface. 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; } } /// /// Adds a new data provider witch a specified name. /// /// /// The method can be used to register a new data provider for further use. /// /// /// /// /// The data provider name. /// An instance of the interface. 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); } /// /// Adds a new data provider. /// /// /// The method can be used to register a new data provider for further use. /// /// /// /// A data provider type. public static void AddDataProvider(Type dataProviderType) { AddDataProvider((DataProviderBase)Activator.CreateInstance(dataProviderType)); } /// /// Adds a new data provider witch a specified name. /// /// /// The method can be used to register a new data provider for further use. /// /// /// /// The data provider name. /// A data provider type. public static void AddDataProvider(string providerName, Type dataProviderType) { AddDataProvider(providerName, (DataProviderBase)Activator.CreateInstance(dataProviderType)); } #endregion #region AddConnectionString /// /// Adds a new connection string or replaces existing one. /// /// /// Use this method when you use only one configuration and /// you don't want to use a configuration file. /// /// /// A valid database connection string. public static void AddConnectionString(string connectionString) { AddConnectionString(string.Empty, connectionString); } /// /// Adds a new connection string or replaces existing one. /// /// /// Use this method when you use multiple configurations and /// you don't want to use a configuration file. /// /// /// The configuration string. /// A valid database connection string. public static void AddConnectionString(string configurationString, string connectionString) { _connectionStringList[configurationString] = connectionString; } /// /// Adds a new connection string or replaces existing one. /// /// /// Use this method when you use multiple configurations and /// you don't want to use a configuration file. /// /// /// The data provider name. /// The configuration string. /// A valid database connection string. 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; /// /// Gets and sets the default configuration string. /// /// /// See the property /// for an explanation and use of the default configuration. /// /// /// A string containing default configuration settings. /// /// public static string DefaultConfiguration { get { if (_defaultConfiguration == null) { // Grab first registered configuration. // foreach (KeyValuePair 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 } }