DbProviderFactories.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbProviderFactories.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. // <owner current="true" primary="false">Microsoft</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.Common {
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Concurrent;
  12. using System.Collections.Generic;
  13. using System.Configuration;
  14. using System.Data;
  15. using System.Diagnostics;
  16. using System.Globalization;
  17. using System.Xml;
  18. using System.Linq;
  19. using System.Reflection;
  20. public static class DbProviderFactories {
  21. private const string AssemblyQualifiedName = "AssemblyQualifiedName";
  22. private const string Instance = "Instance";
  23. private const string InvariantName = "InvariantName";
  24. private const string Name = "Name";
  25. private const string Description = "Description";
  26. private const string InstanceFieldName = "Instance";
  27. private static ConcurrentDictionary<string, ProviderRegistration> _registeredFactories = new ConcurrentDictionary<string, ProviderRegistration>();
  28. private static ConnectionState _initState; // closed, connecting, open
  29. private static DataTable _providerTable;
  30. private static object _lockobj = new object();
  31. static public DbProviderFactory GetFactory(string providerInvariantName) => GetFactory(providerInvariantName, true);
  32. static public DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) {
  33. if (throwOnError)
  34. ADP.CheckArgumentLength(providerInvariantName, "providerInvariantName");
  35. // NOTES: Include the Framework Providers and any other Providers listed in the config file.
  36. DataTable providerTable = GetProviderTable();
  37. if (null != providerTable) {
  38. // we don't need to copy the DataTable because its used in a controlled fashion
  39. // also we don't need to create a blank datatable because we know the information won't exist
  40. #if DEBUG
  41. DataColumn[] pkey = providerTable.PrimaryKey;
  42. Debug.Assert(null != providerTable.Columns[InvariantName], "missing primary key column");
  43. Debug.Assert((null != pkey) && (1 == pkey.Length) && (InvariantName == pkey[0].ColumnName), "bad primary key");
  44. #endif
  45. DataRow providerRow = providerTable.Rows.Find(providerInvariantName);
  46. if (null != providerRow) {
  47. return DbProviderFactories.GetFactory(providerRow);
  48. }
  49. }
  50. if (throwOnError)
  51. throw ADP.ConfigProviderNotFound();
  52. return null;
  53. }
  54. static public DbProviderFactory GetFactory(DataRow providerRow) {
  55. ADP.CheckArgumentNull(providerRow, "providerRow");
  56. // fail with ConfigProviderMissing rather than ColumnNotInTheTable exception
  57. DataColumn column = providerRow.Table.Columns[AssemblyQualifiedName];
  58. if (null != column) {
  59. // column value may not be a string
  60. string assemblyQualifiedName = providerRow[column] as string;
  61. if (!ADP.IsEmpty(assemblyQualifiedName)) {
  62. // FXCop is concerned about the following line call to Get Type,
  63. // If this code is deemed safe during our security review we should add this warning to our exclusion list.
  64. // FXCop Message, pertaining to the call to GetType.
  65. //
  66. // Secure late-binding methods,System.Data.dll!System.Data.Common.DbProviderFactories.GetFactory(System.Data.DataRow):System.Data.Common.DbProviderFactory,
  67. Type providerType = Type.GetType(assemblyQualifiedName);
  68. if (null != providerType) {
  69. System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly|System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static);
  70. if (null != providerInstance) {
  71. Debug.Assert(providerInstance.IsPublic, "field not public");
  72. Debug.Assert(providerInstance.IsStatic, "field not static");
  73. if (providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) {
  74. object factory = providerInstance.GetValue(null);
  75. if (null != factory) {
  76. return (DbProviderFactory)factory;
  77. }
  78. // else throw ConfigProviderInvalid
  79. }
  80. // else throw ConfigProviderInvalid
  81. }
  82. throw ADP.ConfigProviderInvalid();
  83. }
  84. throw ADP.ConfigProviderNotInstalled();
  85. }
  86. // else throw ConfigProviderMissing
  87. }
  88. throw ADP.ConfigProviderMissing();
  89. }
  90. static public DbProviderFactory GetFactory(DbConnection connection) {
  91. ADP.CheckArgumentNull(connection, "connection");
  92. return connection.ProviderFactory;
  93. }
  94. static public DataTable GetFactoryClasses() { // V1.2.3300
  95. // NOTES: Include the Framework Providers and any other Providers listed in the config file.
  96. DataTable dataTable = GetProviderTable();
  97. if (null != dataTable) {
  98. dataTable = dataTable.Copy();
  99. }
  100. else {
  101. dataTable = DbProviderFactoriesConfigurationHandler.CreateProviderDataTable();
  102. }
  103. return dataTable;
  104. }
  105. // VSTFDevDiv # 624213: System.Data.Common.DbProviderFactories.GetFactoryClasses() still gets OracleClient provider in ClientSku environment.
  106. static private DataTable IncludeFrameworkFactoryClasses(DataTable configDataTable)
  107. {
  108. DataTable dataTable = DbProviderFactoriesConfigurationHandler.CreateProviderDataTable();
  109. // NOTES: Adding the following Framework DbProviderFactories
  110. // <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=%ASSEMBLY_VERSION%, Culture=neutral, PublicKeyToken=%ECMA_PUBLICKEY%"/>
  111. // <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=%ASSEMBLY_VERSION%, Culture=neutral, PublicKeyToken=%ECMA_PUBLICKEY%"/>
  112. // <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=%ASSEMBLY_VERSION%, Culture=neutral, PublicKeyToken=%ECMA_PUBLICKEY%"/>
  113. // <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=%ASSEMBLY_VERSION%, Culture=neutral, PublicKeyToken=%ECMA_PUBLICKEY%"/>
  114. Type sysDataType = typeof(System.Data.SqlClient.SqlClientFactory);
  115. string asmQualName = sysDataType.AssemblyQualifiedName.ToString().Replace(DbProviderFactoriesConfigurationHandler.sqlclientPartialAssemblyQualifiedName, DbProviderFactoriesConfigurationHandler.oracleclientPartialAssemblyQualifiedName);
  116. DbProviderFactoryConfigSection[] dbFactoriesConfigSection = new DbProviderFactoryConfigSection[(int)DbProvidersIndex.DbProvidersIndexCount];
  117. dbFactoriesConfigSection[(int)DbProvidersIndex.Odbc] = new DbProviderFactoryConfigSection(typeof(System.Data.Odbc.OdbcFactory), DbProviderFactoriesConfigurationHandler.odbcProviderName, DbProviderFactoriesConfigurationHandler.odbcProviderDescription);
  118. dbFactoriesConfigSection[(int)DbProvidersIndex.OleDb] = new DbProviderFactoryConfigSection(typeof(System.Data.OleDb.OleDbFactory), DbProviderFactoriesConfigurationHandler.oledbProviderName, DbProviderFactoriesConfigurationHandler.oledbProviderDescription);
  119. dbFactoriesConfigSection[(int)DbProvidersIndex.OracleClient] = new DbProviderFactoryConfigSection(DbProviderFactoriesConfigurationHandler.oracleclientProviderName, DbProviderFactoriesConfigurationHandler.oracleclientProviderNamespace, DbProviderFactoriesConfigurationHandler.oracleclientProviderDescription, asmQualName);
  120. dbFactoriesConfigSection[(int)DbProvidersIndex.SqlClient] = new DbProviderFactoryConfigSection(typeof(System.Data.SqlClient.SqlClientFactory), DbProviderFactoriesConfigurationHandler.sqlclientProviderName, DbProviderFactoriesConfigurationHandler.sqlclientProviderDescription);
  121. for (int i = 0; i < dbFactoriesConfigSection.Length; i++)
  122. {
  123. if (dbFactoriesConfigSection[i].IsNull() == false)
  124. {
  125. bool flagIncludeToTable = false;
  126. if (i == ((int)DbProvidersIndex.OracleClient)) // OracleClient Provider: Include only if it installed
  127. {
  128. Type providerType = Type.GetType(dbFactoriesConfigSection[i].AssemblyQualifiedName);
  129. if (providerType != null)
  130. {
  131. // NOTES: Try and create a instance; If it fails, it will throw a System.NullReferenceException exception;
  132. System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
  133. if ((null != providerInstance) && (providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))))
  134. {
  135. Debug.Assert(providerInstance.IsPublic, "field not public");
  136. Debug.Assert(providerInstance.IsStatic, "field not static");
  137. object factory = providerInstance.GetValue(null);
  138. if (null != factory)
  139. {
  140. flagIncludeToTable = true;
  141. } // Else ignore and don't add to table
  142. } // Else ignore and don't add to table
  143. }
  144. }
  145. else
  146. {
  147. flagIncludeToTable = true;
  148. }
  149. if (flagIncludeToTable == true)
  150. {
  151. DataRow row = dataTable.NewRow();
  152. row[Name] = dbFactoriesConfigSection[i].Name;
  153. row[InvariantName] = dbFactoriesConfigSection[i].InvariantName;
  154. row[Description] = dbFactoriesConfigSection[i].Description;
  155. row[AssemblyQualifiedName] = dbFactoriesConfigSection[i].AssemblyQualifiedName;
  156. dataTable.Rows.Add(row);
  157. } // Else Ignore and do not include to table;
  158. }
  159. }
  160. // NOTES: Additional step added here to maintain the sequence order of the providers listed.
  161. // The Framework Providers get listed first and is followed the custom providers.
  162. for (int i = 0; (configDataTable != null) && (i < configDataTable.Rows.Count); i++)
  163. {
  164. try
  165. {
  166. bool flagIncludeToTable = false;
  167. // OracleClient Provider: Include only if it installed
  168. if (configDataTable.Rows[i][AssemblyQualifiedName].ToString().ToLowerInvariant().Contains(DbProviderFactoriesConfigurationHandler.oracleclientProviderNamespace.ToString().ToLowerInvariant()))
  169. {
  170. Type providerType = Type.GetType(configDataTable.Rows[i][AssemblyQualifiedName].ToString());
  171. if (providerType != null)
  172. {
  173. // NOTES: Try and create a instance; If it fails, it will throw a System.NullReferenceException exception;
  174. System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
  175. if ((null != providerInstance) && (providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))))
  176. {
  177. Debug.Assert(providerInstance.IsPublic, "field not public");
  178. Debug.Assert(providerInstance.IsStatic, "field not static");
  179. object factory = providerInstance.GetValue(null);
  180. if (null != factory)
  181. {
  182. flagIncludeToTable = true;
  183. } // Else ignore and don't add to table
  184. } // Else ignore and don't add to table
  185. }
  186. }
  187. else
  188. {
  189. flagIncludeToTable = true;
  190. }
  191. if(flagIncludeToTable == true)
  192. {
  193. // NOTES: If it already exist in the configTable, it raises a ConstraintException;
  194. dataTable.Rows.Add(configDataTable.Rows[i].ItemArray);
  195. }
  196. }
  197. catch (System.Data.ConstraintException)
  198. {
  199. // NOTES: Ignore item; Already exist in the configTable, hence the ConstraintException; Move to the next item;
  200. }
  201. }
  202. return dataTable;
  203. }
  204. static private DataTable GetProviderTable() {
  205. Initialize();
  206. return _providerTable;
  207. }
  208. static private void Initialize() {
  209. if (ConnectionState.Open != _initState) {
  210. lock (_lockobj) {
  211. switch(_initState) {
  212. case ConnectionState.Closed:
  213. _initState = ConnectionState.Connecting; // used for preventing recursion
  214. try {
  215. DataSet configTable = PrivilegedConfigurationManager.GetSection(DbProviderFactoriesConfigurationHandler.sectionName) as DataSet;
  216. _providerTable = (null != configTable) ? IncludeFrameworkFactoryClasses(configTable.Tables[DbProviderFactoriesConfigurationHandler.providerGroup]) : IncludeFrameworkFactoryClasses(null);
  217. }
  218. finally {
  219. _initState = ConnectionState.Open;
  220. }
  221. break;
  222. case ConnectionState.Connecting:
  223. case ConnectionState.Open:
  224. break;
  225. default:
  226. Debug.Assert(false, "unexpected state");
  227. break;
  228. }
  229. }
  230. }
  231. }
  232. #if MONO
  233. public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory)
  234. {
  235. factory = GetFactory(providerInvariantName, throwOnError: false);
  236. return factory != null;
  237. }
  238. public static IEnumerable<string> GetProviderInvariantNames()
  239. {
  240. return _registeredFactories.Keys.ToList();
  241. }
  242. public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName)
  243. {
  244. ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName));
  245. ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName));
  246. // this method performs a deferred registration: the type name specified is checked when the factory is requested for the first time.
  247. _registeredFactories[providerInvariantName] = new ProviderRegistration(factoryTypeAssemblyQualifiedName, null);
  248. }
  249. private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass)
  250. {
  251. ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass));
  252. if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory)))
  253. {
  254. throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName));
  255. }
  256. FieldInfo providerInstance = providerFactoryClass.GetField(InstanceFieldName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
  257. if (null == providerInstance)
  258. {
  259. throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance);
  260. }
  261. if (!providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory)))
  262. {
  263. throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance);
  264. }
  265. object factory = providerInstance.GetValue(null);
  266. if (null == factory)
  267. {
  268. throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance);
  269. }
  270. return (DbProviderFactory)factory;
  271. }
  272. public static void RegisterFactory(string providerInvariantName, Type providerFactoryClass)
  273. {
  274. RegisterFactory(providerInvariantName, GetFactoryInstance(providerFactoryClass));
  275. }
  276. public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory)
  277. {
  278. ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName));
  279. ADP.CheckArgumentNull(factory, nameof(factory));
  280. _registeredFactories[providerInvariantName] = new ProviderRegistration(factory.GetType().AssemblyQualifiedName, factory);
  281. }
  282. public static bool UnregisterFactory(string providerInvariantName)
  283. {
  284. return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out _);
  285. }
  286. private struct ProviderRegistration
  287. {
  288. internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProviderFactory factoryInstance)
  289. {
  290. this.FactoryTypeAssemblyQualifiedName = factoryTypeAssemblyQualifiedName;
  291. this.FactoryInstance = factoryInstance;
  292. }
  293. internal string FactoryTypeAssemblyQualifiedName { get; }
  294. /// <summary>
  295. /// The cached instance of the type in <see cref="FactoryTypeAssemblyQualifiedName"/>. If null, this registation is seen as a deferred registration
  296. /// and <see cref="FactoryTypeAssemblyQualifiedName"/> is checked the first time when this registration is requested through GetFactory().
  297. /// </summary>
  298. internal DbProviderFactory FactoryInstance { get; }
  299. }
  300. #endif
  301. }
  302. }