Mercurial > pub > bltoolkit
view Source/Data/DbManager.Config.cs @ 4:f757da6161a1
!bug 100 + 2h fixed gregression
author | cin |
---|---|
date | Sun, 24 Aug 2014 17:57:42 +0400 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.Linq; namespace BLToolkit.Data { using Configuration; 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); var 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 Sql2012DataProvider()); AddDataProvider(new Sql2008DataProvider()); AddDataProvider(new Sql2005DataProvider()); AddDataProvider(new SqlDataProvider()); AddDataProvider(new Sql2000DataProvider()); AddDataProvider(new AccessDataProvider()); AddDataProvider(new OleDbDataProvider()); AddDataProvider(new OdbcDataProvider()); var section = BLToolkitSection.Instance; if (section != null) { _defaultConfiguration = section.DefaultConfiguration; foreach (DataProviderElement provider in section.DataProviders) { var dataProviderType = Type.GetType(provider.TypeName, true); var 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; } } var dataProviders = ConfigurationManager.AppSettings.Get("BLToolkit.DataProviders"); if (dataProviders != null) { if (TraceSwitch.TraceWarning) WriteTraceLine("Using appSettings\\BLToolkit.DataProviders is obsolete. Consider using bltoolkit configuration section instead.", TraceSwitch.DisplayName); foreach (var 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; } volatile 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"); var 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; var dp = (DataProviderBase)_configurationList[configurationString]; if (dp == null) { var css = ConfigurationManager.ConnectionStrings[configurationString]; if (css != null && !string.IsNullOrEmpty(css.ProviderName)) { string provider = null; if (css.ProviderName == "System.Data.SqlClient") { try { using (SqlConnection sqlConnection = new SqlConnection(css.ConnectionString)) { sqlConnection.Open(); string serverVersion = sqlConnection.ServerVersion; string[] serverVersionDetails = serverVersion.Split(new string[] {"."}, StringSplitOptions.None); int versionNumber = int.Parse(serverVersionDetails[0]); switch (versionNumber) { case 8: provider = "MSSQL2000"; break; case 9: provider = "MSSQL2005"; break; //MSSQL 2005 -> Can the same as 2008 case 10: provider = "MSSQL2008"; break; case 11: provider = "MSSQL2012"; break; default: provider = "MSSQL2008"; break; } } } catch (Exception) {} } if (provider == null) { // This hack should be redone. // provider = css.ProviderName == "System.Data.SqlClient" ? configurationString.IndexOf("2012") >= 0 ? "MSSQL2012" : configurationString.IndexOf("2008") >= 0 ? "MSSQL2008" : configurationString.IndexOf("2000") >= 0 ? "MSSQL2000" : 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 // var cs = configurationString.ToUpper(); var key = _defaultDataProviderName; if (cs.Length > 0) { cs += ProviderNameDivider; foreach (var 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) { var cs = (string)_anyProviderConfigurationList[configurationString]; var searchRequired = (cs == null); if (searchRequired) { var csWithoutProvider = configurationString.Substring(AnyProvider.Length); if (configurationString.Length == 0) throw new ArgumentNullException("configurationString"); foreach (var 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 (var name in ConfigurationManager.AppSettings.AllKeys) { if (name.StartsWith("ConnectionString" + ProviderNameDivider)) { var str = name.Substring(name.IndexOf(ProviderNameDivider) + ProviderNameDivider.Length); if (IsMatchedConfigurationString(str, csWithoutProvider)) { cs = str; break; } } } } if (cs == null) cs = csWithoutProvider; } var 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. // var key = string.Format("ConnectionString{0}{1}", configurationString.Length == 0? String.Empty: ProviderNameDivider, configurationString); var 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. // var defaultConfiguration = _connectionStringList.Select(de => de.Key).FirstOrDefault(); 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; } } } _defaultConfiguration = defaultConfiguration; } return _defaultConfiguration; } set { _defaultConfiguration = value; } } #endregion } }