This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of DbProviderFactories. (#25410)
* Implements #19826: DbConnection doesn't offer an internal property ProviderFactory * Added test for #19826. Due to project misconfiguration it fails locally. * Removed writeline * Added comment to illustrate the purpose of the property which currently is dead code. * Implementation of DbProviderFactories, ported from NetFx and adjusted to CoreFx See #4571, #19825 and #19826 * Update for project files to get a netcoreapp test set * Changes to project system to get the stuff compiled. Failed. * Various changes: - Updated config/csproj files for netcoreapp for tests. - Properly moved DbProviderFactories file to right location - Added DbProviderFactories type to ref project, split across 2 files (one for netcoreapp specific methods) - Changed SetFactory to ConfigureFactory - Changed generic type argument to normal method parameter - Removed default parameter values and added overloads * Added tests for DbProviderFactories. * Better subclass testing added and more tests added * Moved all hard-coded strings to SR/Strings.resx. Added test for factory retrieval using a connection * Removal of now redundant comment. See: #19885 (review) * Comment correction for bad English * Refactored API a bit, see: dotnet/standard#356 (comment) * Added tests for reworked API. Added tests for wrong type passing * Reverting change in sln file: See #19885 (review) * Almost done implementation using DataTable internal storage. Refactoring now to concurrentdictionary * Final work for #20903 * Corrections of the implementation-> - registrations of assembly type name are now deferred - storage is now a struct, as the typename has to be registrated as well. - corrected a wrong nameof() call. - added tests for the change behavior in RegisterFactory(string, string) * Final implementation * Corrections made to code by request of @saurabh500 * Github for windows didn't commit this file in last commit... :/ * Again correcting component elements. These are automatically inserted so if they're back again, I can't remove them * ... annnnd another component element. Last one I hope * @divega requested changes - Corrected sln file netcoreapp->netstandard - Corrected wording in exception messages. - Component elements are back, I'll leave them now, they get back regardless. - Renamed column name constants to have the 'ColumnName' suffix for more clarity - Made Instance constant be initialized with nameof(Instance) - Added using for System.Reflection so less verbose code * More @divega requested changes - factored out a clone into its own method (GetProviderTypeFromTypeName) - Reverted 'nameof(Instance)' to string, and renamed field - Removed redundant null/length check in ctor of ProviderRegistration ctor * Last nit removal for @divega
- Loading branch information
1 parent
5cbb754
commit 7caa955
Showing
11 changed files
with
544 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Changes to this file must follow the http://aka.ms/api-review process. | ||
// ------------------------------------------------------------------------------ | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace System.Data.Common | ||
{ | ||
public static partial class DbProviderFactories | ||
{ | ||
public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) { throw null; } | ||
public static void RegisterFactory(string providerInvariantName, Type factoryType) { throw null; } | ||
public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory) { throw null; } | ||
public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) { throw null; } | ||
public static bool UnregisterFactory(string providerInvariantName) { throw null; } | ||
public static IEnumerable<string> GetProviderInvariantNames() { throw null; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
200 changes: 200 additions & 0 deletions
200
src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading; | ||
|
||
namespace System.Data.Common | ||
{ | ||
public static partial class DbProviderFactories | ||
{ | ||
private struct ProviderRegistration | ||
{ | ||
internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProviderFactory factoryInstance) | ||
{ | ||
this.FactoryTypeAssemblyQualifiedName = factoryTypeAssemblyQualifiedName; | ||
this.FactoryInstance = factoryInstance; | ||
} | ||
|
||
internal string FactoryTypeAssemblyQualifiedName { get; } | ||
/// <summary> | ||
/// The cached instance of the type in <see cref="FactoryTypeAssemblyQualifiedName"/>. If null, this registation is seen as a deferred registration | ||
/// and <see cref="FactoryTypeAssemblyQualifiedName"/> is checked the first time when this registration is requested through GetFactory(). | ||
/// </summary> | ||
internal DbProviderFactory FactoryInstance { get; } | ||
} | ||
|
||
private static ConcurrentDictionary<string, ProviderRegistration> _registeredFactories = new ConcurrentDictionary<string, ProviderRegistration>(); | ||
private const string AssemblyQualifiedNameColumnName = "AssemblyQualifiedName"; | ||
private const string InvariantNameColumnName = "InvariantName"; | ||
private const string NameColumnName = "Name"; | ||
private const string DescriptionColumnName = "Description"; | ||
private const string ProviderGroupColumnName = "DbProviderFactories"; | ||
private const string InstanceFieldName = "Instance"; | ||
|
||
public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) | ||
{ | ||
factory = GetFactory(providerInvariantName, throwOnError: false); | ||
return factory != null; | ||
} | ||
|
||
public static DbProviderFactory GetFactory(string providerInvariantName) | ||
{ | ||
return GetFactory(providerInvariantName, throwOnError: true); | ||
} | ||
|
||
public static DbProviderFactory GetFactory(DataRow providerRow) | ||
{ | ||
ADP.CheckArgumentNull(providerRow, nameof(providerRow)); | ||
|
||
DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedNameColumnName]; | ||
if (null == assemblyQualifiedNameColumn) | ||
{ | ||
throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); | ||
} | ||
|
||
string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; | ||
if (string.IsNullOrWhiteSpace(assemblyQualifiedName)) | ||
{ | ||
throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); | ||
} | ||
|
||
return GetFactoryInstance(GetProviderTypeFromTypeName(assemblyQualifiedName)); | ||
} | ||
|
||
|
||
public static DbProviderFactory GetFactory(DbConnection connection) | ||
{ | ||
ADP.CheckArgumentNull(connection, nameof(connection)); | ||
|
||
return connection.ProviderFactory; | ||
} | ||
|
||
public static DataTable GetFactoryClasses() | ||
{ | ||
DataColumn nameColumn = new DataColumn(NameColumnName, typeof(string)) { ReadOnly = true }; | ||
DataColumn descriptionColumn = new DataColumn(DescriptionColumnName, typeof(string)) { ReadOnly = true }; | ||
DataColumn invariantNameColumn = new DataColumn(InvariantNameColumnName, typeof(string)) { ReadOnly = true }; | ||
DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedNameColumnName, typeof(string)) { ReadOnly = true }; | ||
|
||
DataTable toReturn = new DataTable(ProviderGroupColumnName) { Locale = CultureInfo.InvariantCulture }; | ||
toReturn.Columns.AddRange(new[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }); | ||
toReturn.PrimaryKey = new[] { invariantNameColumn }; | ||
foreach(var kvp in _registeredFactories) | ||
{ | ||
DataRow newRow = toReturn.NewRow(); | ||
newRow[InvariantNameColumnName] = kvp.Key; | ||
newRow[AssemblyQualifiedNameColumnName] = kvp.Value.FactoryTypeAssemblyQualifiedName; | ||
newRow[NameColumnName] = string.Empty; | ||
newRow[DescriptionColumnName] = string.Empty; | ||
toReturn.AddRow(newRow); | ||
} | ||
return toReturn; | ||
} | ||
|
||
public static IEnumerable<string> GetProviderInvariantNames() | ||
{ | ||
return _registeredFactories.Keys.ToList(); | ||
} | ||
|
||
public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) | ||
{ | ||
ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); | ||
ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName)); | ||
|
||
// this method performs a deferred registration: the type name specified is checked when the factory is requested for the first time. | ||
_registeredFactories[providerInvariantName] = new ProviderRegistration(factoryTypeAssemblyQualifiedName, null); | ||
} | ||
|
||
public static void RegisterFactory(string providerInvariantName, Type providerFactoryClass) | ||
{ | ||
RegisterFactory(providerInvariantName, GetFactoryInstance(providerFactoryClass)); | ||
} | ||
|
||
public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory) | ||
{ | ||
ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); | ||
ADP.CheckArgumentNull(factory, nameof(factory)); | ||
|
||
_registeredFactories[providerInvariantName] = new ProviderRegistration(factory.GetType().AssemblyQualifiedName, factory); | ||
} | ||
|
||
public static bool UnregisterFactory(string providerInvariantName) | ||
{ | ||
return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out _); | ||
} | ||
|
||
private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) | ||
{ | ||
if (throwOnError) | ||
{ | ||
ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); | ||
} | ||
else | ||
{ | ||
if (string.IsNullOrWhiteSpace(providerInvariantName)) | ||
{ | ||
return null; | ||
} | ||
} | ||
bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out ProviderRegistration registration); | ||
if (!wasRegistered) | ||
{ | ||
return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; | ||
} | ||
DbProviderFactory toReturn = registration.FactoryInstance; | ||
if (toReturn == null) | ||
{ | ||
// Deferred registration, do checks now on the type specified and register instance in storage. | ||
// Even in the case of throwOnError being false, this will throw when an exception occurs checking the registered type as the user has to be notified the | ||
// registration is invalid, even though the registration is there. | ||
toReturn = GetFactoryInstance(GetProviderTypeFromTypeName(registration.FactoryTypeAssemblyQualifiedName)); | ||
RegisterFactory(providerInvariantName, toReturn); | ||
} | ||
return toReturn; | ||
} | ||
|
||
private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) | ||
{ | ||
ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); | ||
if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) | ||
{ | ||
throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); | ||
} | ||
|
||
FieldInfo providerInstance = providerFactoryClass.GetField(InstanceFieldName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); | ||
if (null == providerInstance) | ||
{ | ||
throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); | ||
} | ||
if (!providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) | ||
{ | ||
throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); | ||
} | ||
object factory = providerInstance.GetValue(null); | ||
if (null == factory) | ||
{ | ||
throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); | ||
} | ||
return (DbProviderFactory)factory; | ||
} | ||
|
||
|
||
private static Type GetProviderTypeFromTypeName(string assemblyQualifiedName) | ||
{ | ||
Type providerType = Type.GetType(assemblyQualifiedName); | ||
if (null == providerType) | ||
{ | ||
throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); | ||
} | ||
return providerType; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.