-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Implementation of DbProviderFactories. #25410
Changes from all commits
5fe21a9
7074007
931f54c
7102d7c
2edb985
ff76cef
03b9f72
6452411
bc5badf
8516a0b
6415bc6
9473284
b7d78cf
920a0ba
c37583e
9168ac9
bc836c5
b83c67e
f84c3a8
356f393
bf607f2
00cfd77
4b1fdcf
8872282
89f73b2
d19cf6a
df9d929
f79c029
6868673
8d9834f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; } | ||
} | ||
} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add xml docs? The .NET Framework ones should be a good starting point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought those weren't required? (as they're added elsewhere?) That's why I didn't add them There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure. @karelz, @saurabh500? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at random other classes in this solution, none have xml doc comments. |
||
{ | ||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: collapse into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hate 'else if' as it has an 'else' block without curly braces, which I find personally not a good choice, every block should have curly braces, even if it's just 1 statement. |
||
{ | ||
return null; | ||
} | ||
} | ||
bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out ProviderRegistration registration); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: inline/remove wasRegistered variable if possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't possible as the 'registration' variable is declared at that line, so inlining it makes it go into the scope of the if true block, while it's used after that as well :) This can be solved then by declaring a line with the registration variable and that is mitigating the whole inlining act ;) |
||
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; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpicking here, but are all these subtype tags needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was already in the csproj, so I think @saurabh500 might know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The
<SubType>Component</SubType>
was added by VS .Can you remove these tags please ? I wish I knew a way to turn off their generation in VS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They keep popping back up, so I left them for now, it's of no use to get rid of them :(