Mercurial > pub > bltoolkit
diff Source/Data/DbManager.Config.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/DbManager.Config.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,782 @@ +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 + } +}