From 5fe21a91c1b00708c7be65222ccd9d3c38d73110 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Wed, 17 May 2017 12:24:48 +0200 Subject: [PATCH 01/27] Implements #19826: DbConnection doesn't offer an internal property ProviderFactory --- src/System.Data.Common/src/System/Data/Common/DbConnection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs index f3c2895e558e..52cb38f86fca 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs @@ -35,6 +35,8 @@ protected DbConnection() : base() /// protected virtual DbProviderFactory DbProviderFactory => null; + internal DbProviderFactory ProviderFactory=>DbProviderFactory; + [Browsable(false)] public abstract string ServerVersion { get; } From 7074007bf56057707de41daf1937982c1f7da0b8 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Thu, 18 May 2017 14:47:49 +0200 Subject: [PATCH 02/27] Added test for #19826. Due to project misconfiguration it fails locally. --- .../src/System/Data/Common/DbConnection.cs | 2 +- .../System/Data/Common/DbConnectionTests.cs | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs index 52cb38f86fca..fc78b396d292 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs @@ -35,7 +35,7 @@ protected DbConnection() : base() /// protected virtual DbProviderFactory DbProviderFactory => null; - internal DbProviderFactory ProviderFactory=>DbProviderFactory; + internal DbProviderFactory ProviderFactory => DbProviderFactory; [Browsable(false)] public abstract string ServerVersion { get; } diff --git a/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs b/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs index e64901dc38f5..0570f722ddb5 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // See the LICENSE file in the project root for more information. +using System.Reflection; using Xunit; namespace System.Data.Common.Tests @@ -94,6 +95,47 @@ protected override DbCommand CreateDbCommand() } } + private class DbProviderFactoryConnection : DbConnection + { + protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) + { + throw new NotImplementedException(); + } + + public override void ChangeDatabase(string databaseName) + { + throw new NotImplementedException(); + } + + public override void Close() + { + throw new NotImplementedException(); + } + + public override void Open() + { + throw new NotImplementedException(); + } + + public override string ConnectionString { get; set; } + public override string Database { get; } + public override ConnectionState State { get; } + public override string DataSource { get; } + public override string ServerVersion { get; } + + protected override DbCommand CreateDbCommand() + { + throw new NotImplementedException(); + } + + protected override DbProviderFactory DbProviderFactory => TestDbProviderFactory.Instance; + } + + private class TestDbProviderFactory : DbProviderFactory + { + public static DbProviderFactory Instance = new TestDbProviderFactory(); + } + [Fact] public void CanBeFinalized() { @@ -102,5 +144,18 @@ public void CanBeFinalized() GC.WaitForPendingFinalizers(); Assert.True(_wasFinalized); } + + [Fact] + public void ProviderFactoryTest() + { + DbProviderFactoryConnection con = new DbProviderFactoryConnection(); + PropertyInfo providerFactoryProperty = con.GetType().GetProperty("ProviderFactory", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(providerFactoryProperty); + Console.WriteLine("providerFactoryProperty not null"); + DbProviderFactory factory = providerFactoryProperty.GetValue(con) as DbProviderFactory; + Assert.NotNull(factory); + Assert.Same(typeof(TestDbProviderFactory), factory.GetType()); + Assert.Same(TestDbProviderFactory.Instance, factory); + } } } From 931f54cfeb36243ec2d37dfe524271fce5507bcb Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Thu, 18 May 2017 14:48:52 +0200 Subject: [PATCH 03/27] Removed writeline --- .../tests/System/Data/Common/DbConnectionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs b/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs index 0570f722ddb5..7cf223f74328 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs @@ -151,7 +151,6 @@ public void ProviderFactoryTest() DbProviderFactoryConnection con = new DbProviderFactoryConnection(); PropertyInfo providerFactoryProperty = con.GetType().GetProperty("ProviderFactory", BindingFlags.NonPublic | BindingFlags.Instance); Assert.NotNull(providerFactoryProperty); - Console.WriteLine("providerFactoryProperty not null"); DbProviderFactory factory = providerFactoryProperty.GetValue(con) as DbProviderFactory; Assert.NotNull(factory); Assert.Same(typeof(TestDbProviderFactory), factory.GetType()); From 7102d7c97fef3d0754bf8dff5472e4420ed00ce1 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Thu, 18 May 2017 17:04:46 +0200 Subject: [PATCH 04/27] Added comment to illustrate the purpose of the property which currently is dead code. --- src/System.Data.Common/src/System/Data/Common/DbConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs index fc78b396d292..12a7deb023f6 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs @@ -35,7 +35,7 @@ protected DbConnection() : base() /// protected virtual DbProviderFactory DbProviderFactory => null; - internal DbProviderFactory ProviderFactory => DbProviderFactory; + internal DbProviderFactory ProviderFactory => DbProviderFactory; // This property is required for DbProviderFactories' API and will appear as 'dead' code till that class has been ported. [Browsable(false)] public abstract string ServerVersion { get; } From 2edb9855d1c849ea6855ee6b1ded565375fe4ecc Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 19 May 2017 17:21:04 +0200 Subject: [PATCH 05/27] Implementation of DbProviderFactories, ported from NetFx and adjusted to CoreFx See #4571, #19825 and #19826 --- .../ref/System.Data.Common.cs | 9 + .../src/System.Data.Common.csproj | 45 +++-- .../System/Data/Common/DbProviderFactories.cs | 169 ++++++++++++++++++ .../tests/System.Data.Common.Tests.csproj | 7 +- .../Data/Common/DbProviderFactoriesTests.cs | 19 ++ 5 files changed, 236 insertions(+), 13 deletions(-) create mode 100644 src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs create mode 100644 src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs diff --git a/src/System.Data.Common/ref/System.Data.Common.cs b/src/System.Data.Common/ref/System.Data.Common.cs index 854d72c0bb4c..ef7141ef6908 100644 --- a/src/System.Data.Common/ref/System.Data.Common.cs +++ b/src/System.Data.Common/ref/System.Data.Common.cs @@ -2211,6 +2211,15 @@ protected DbProviderFactory() { } public virtual System.Data.Common.DbDataSourceEnumerator CreateDataSourceEnumerator() { throw null; } public virtual System.Data.Common.DbParameter CreateParameter() { throw null; } } + public static partial class DbProviderFactories + { + public static DbProviderFactory GetFactory(string providerInvariantName) { throw null; } + public static DbProviderFactory GetFactory(DataRow providerRow) { throw null; } + public static DbProviderFactory GetFactory(DbConnection connection) { throw null; } + public static void SetFactory(string name="", string providerInvariantName="", string description = "") where TFactory : DbProviderFactory { throw null; } + public static void SetFactory(DbConnection connection, string name="", string providerInvariantName = "", string description = "") { throw null; } + public static DataTable GetFactoryClasses() { throw null; } + } [System.AttributeUsageAttribute((System.AttributeTargets)(128), AllowMultiple = false, Inherited = true)] public sealed partial class DbProviderSpecificTypePropertyAttribute : System.Attribute { diff --git a/src/System.Data.Common/src/System.Data.Common.csproj b/src/System.Data.Common/src/System.Data.Common.csproj index 9db26627e0bc..6f92d8ce88a1 100644 --- a/src/System.Data.Common/src/System.Data.Common.csproj +++ b/src/System.Data.Common/src/System.Data.Common.csproj @@ -16,6 +16,7 @@ + @@ -55,7 +56,9 @@ - + + Component + @@ -76,10 +79,14 @@ - + + Component + - + + Component + @@ -89,9 +96,13 @@ - + + Component + - + + Component + @@ -133,7 +144,9 @@ - + + Component + @@ -158,7 +171,9 @@ - + + Component + @@ -168,9 +183,15 @@ - - - + + Component + + + Component + + + Component + System\Data\Common\DbConnectionPoolKey.cs @@ -178,7 +199,9 @@ - + + Component + diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs new file mode 100644 index 000000000000..64f341fea72e --- /dev/null +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -0,0 +1,169 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Threading; + +namespace System.Data.Common +{ + public static partial class DbProviderFactories + { + private const string AssemblyQualifiedName = "AssemblyQualifiedName"; + private const string InvariantName = "InvariantName"; + private const string Name = "Name"; + private const string Description = "Description"; + private const string ProviderGroup = "DbProviderFactories"; + private const string Instance = "Instance"; + + private static DataTable _providerTable = GetInitialProviderTable(); + private static ReaderWriterLockSlim _providerTableLock = new ReaderWriterLockSlim(); + + public static DbProviderFactory GetFactory(string providerInvariantName) + { + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + try + { + _providerTableLock.EnterReadLock(); + if (null != _providerTable) + { + DataRow providerRow = _providerTable.Rows.Find(providerInvariantName); + if (null != providerRow) + { + return DbProviderFactories.GetFactory(providerRow); + } + } + throw ADP.Argument($"The specified invariant name '{providerInvariantName}' wasn't found in the list of registered .Net Framework Data Providers.", providerInvariantName); + } + finally + { + _providerTableLock.ExitReadLock(); + } + } + + public static DbProviderFactory GetFactory(DataRow providerRow) + { + ADP.CheckArgumentNull(providerRow, nameof(providerRow)); + // no need for locking on the table, as the row can only be from a DataTable that's a copy of the one contained in this class. + DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName]; + if (null != assemblyQualifiedNameColumn) + { + // column value may not be a string + string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; + if (!string.IsNullOrWhiteSpace(assemblyQualifiedName)) + { + Type providerType = Type.GetType(assemblyQualifiedName); + if (null != providerType) + { + System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (null != providerInstance) + { + if (providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) + { + object factory = providerInstance.GetValue(null); + if (null != factory) + { + return (DbProviderFactory)factory; + } + // else throw DataProviderInvalid + } + // else throw DataProviderInvalid + } + throw ADP.InvalidOperation("The requested .Net Framework Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type."); + } + throw ADP.Argument($"The registered .Net Framework Data Provider's DbProviderFactory implementation type '{assemblyQualifiedName}' couldn't be loaded."); + } + } + throw ADP.Argument("The missing .Net Framework Data Provider's assembly qualified name is required."); + } + + public static DbProviderFactory GetFactory(DbConnection connection) + { + ADP.CheckArgumentNull(connection, nameof(connection)); + return connection.ProviderFactory; + } + + public static void SetFactory(string name="", string providerInvariantName="", string description = "") + where TFactory : DbProviderFactory + { + RegisterFactoryInTable(name, providerInvariantName, typeof(TFactory), description); + } + + public static void SetFactory(DbConnection connection, string name="", string providerInvariantName = "", string description = "") + { + ADP.CheckArgumentNull(connection, nameof(connection)); + DbProviderFactory factoryInstance = GetFactory(connection); + if (factoryInstance == null) + { + throw ADP.Argument("The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection"); + } + RegisterFactoryInTable(name, providerInvariantName, factoryInstance.GetType(), description); + } + + public static DataTable GetFactoryClasses() + { + try + { + _providerTableLock.EnterReadLock(); + return _providerTable == null ? GetInitialProviderTable() : _providerTable.Copy(); + } + finally + { + _providerTableLock.ExitReadLock(); + } + } + + private static void RegisterFactoryInTable(string name, string providerInvariantName, Type factoryType, string description) + { + ADP.CheckArgumentNull(factoryType, nameof(factoryType)); + string invariantNameToUse = string.IsNullOrWhiteSpace(providerInvariantName) ? factoryType.Namespace : providerInvariantName; + + try + { + _providerTableLock.EnterWriteLock(); + + bool newRow = false; + DataRow rowToAlter = _providerTable.Rows.Find(invariantNameToUse); + if (rowToAlter == null) + { + newRow = true; + rowToAlter = _providerTable.NewRow(); + rowToAlter[Name] = string.IsNullOrWhiteSpace(name) ? invariantNameToUse : name; + rowToAlter[InvariantName] = invariantNameToUse; + rowToAlter[Description] = description ?? string.Empty; + } + rowToAlter[AssemblyQualifiedName] = factoryType.AssemblyQualifiedName; + if (newRow) + { + _providerTable.AddRow(rowToAlter); + } + } + finally + { + _providerTableLock.ExitWriteLock(); + } + } + + private static DataTable GetInitialProviderTable() + { + DataColumn nameColumn = new DataColumn(Name, typeof(string)); + nameColumn.ReadOnly = true; + DataColumn descriptionColumn = new DataColumn(Description, typeof(string)); + descriptionColumn.ReadOnly = true; + DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)); + invariantNameColumn.ReadOnly = true; + // This column isn't readonly in CoreFx, while it is readonly in NetFx, as this class allow overwriting registered providers at runtime. + DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)); + + DataColumn[] primaryKey = new DataColumn[] { invariantNameColumn }; + DataColumn[] columns = new DataColumn[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }; + DataTable initialTable = new DataTable(ProviderGroup); + initialTable.Locale = CultureInfo.InvariantCulture; + initialTable.Columns.AddRange(columns); + initialTable.PrimaryKey = primaryKey; + return initialTable; + } + } +} diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index 1a166915247b..06e0155136e1 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -20,6 +20,7 @@ + @@ -73,7 +74,9 @@ - + + Component + @@ -118,4 +121,4 @@ - + \ No newline at end of file diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs new file mode 100644 index 000000000000..98ec8dd058d5 --- /dev/null +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Data.Common; +using Xunit; + +namespace System.Data.Common +{ + public class DbProviderFactoriesTests + { + [Fact] + [Trait("Issue", "I19826")] + public void InitializationTest() + { + DataTable initializedTable = DbProviderFactories.GetFactoryClasses(); + } + } +} From ff76cefe9e289ae6b2e938b4e19531bfc553b87b Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Tue, 23 May 2017 11:37:01 +0200 Subject: [PATCH 06/27] Update for project files to get a netcoreapp test set --- src/System.Data.Common/System.Data.Common.sln | 4 ++-- src/System.Data.Common/ref/System.Data.Common.cs | 9 --------- src/System.Data.Common/tests/Configurations.props | 1 + .../tests/System.Data.Common.Tests.csproj | 6 +++++- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/System.Data.Common/System.Data.Common.sln b/src/System.Data.Common/System.Data.Common.sln index 849ecb5f5fba..816b5fd8bf29 100644 --- a/src/System.Data.Common/System.Data.Common.sln +++ b/src/System.Data.Common/System.Data.Common.sln @@ -26,8 +26,8 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU diff --git a/src/System.Data.Common/ref/System.Data.Common.cs b/src/System.Data.Common/ref/System.Data.Common.cs index ef7141ef6908..854d72c0bb4c 100644 --- a/src/System.Data.Common/ref/System.Data.Common.cs +++ b/src/System.Data.Common/ref/System.Data.Common.cs @@ -2211,15 +2211,6 @@ protected DbProviderFactory() { } public virtual System.Data.Common.DbDataSourceEnumerator CreateDataSourceEnumerator() { throw null; } public virtual System.Data.Common.DbParameter CreateParameter() { throw null; } } - public static partial class DbProviderFactories - { - public static DbProviderFactory GetFactory(string providerInvariantName) { throw null; } - public static DbProviderFactory GetFactory(DataRow providerRow) { throw null; } - public static DbProviderFactory GetFactory(DbConnection connection) { throw null; } - public static void SetFactory(string name="", string providerInvariantName="", string description = "") where TFactory : DbProviderFactory { throw null; } - public static void SetFactory(DbConnection connection, string name="", string providerInvariantName = "", string description = "") { throw null; } - public static DataTable GetFactoryClasses() { throw null; } - } [System.AttributeUsageAttribute((System.AttributeTargets)(128), AllowMultiple = false, Inherited = true)] public sealed partial class DbProviderSpecificTypePropertyAttribute : System.Attribute { diff --git a/src/System.Data.Common/tests/Configurations.props b/src/System.Data.Common/tests/Configurations.props index c398e42e8994..1ea25def2d92 100644 --- a/src/System.Data.Common/tests/Configurations.props +++ b/src/System.Data.Common/tests/Configurations.props @@ -3,6 +3,7 @@ netstandard; + netcoreapp; \ No newline at end of file diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index 06e0155136e1..feff048ef4ec 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -7,6 +7,8 @@ + + @@ -20,7 +22,6 @@ - @@ -117,6 +118,9 @@ System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs + + + From 03b9f726b07da37bbb2296cc47b30db1ec7303ac Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Wed, 31 May 2017 10:46:18 +0200 Subject: [PATCH 07/27] Changes to project system to get the stuff compiled. Failed. --- src/System.Data.Common/System.Data.Common.sln | 8 ++++---- src/System.Data.Common/tests/Configurations.props | 3 ++- .../tests/System.Data.Common.Tests.csproj | 9 ++++++--- ...esTests.cs => DbProviderFactoriesTests.netcoreapp.cs} | 0 4 files changed, 12 insertions(+), 8 deletions(-) rename src/System.Data.Common/tests/System/Data/Common/{DbProviderFactoriesTests.cs => DbProviderFactoriesTests.netcoreapp.cs} (100%) diff --git a/src/System.Data.Common/System.Data.Common.sln b/src/System.Data.Common/System.Data.Common.sln index 816b5fd8bf29..47c84cae391e 100644 --- a/src/System.Data.Common/System.Data.Common.sln +++ b/src/System.Data.Common/System.Data.Common.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.Common.Tests", "tests\System.Data.Common.Tests.csproj", "{B473F77D-4168-4123-932A-E88020B768FA}" ProjectSection(ProjectDependencies) = postProject @@ -26,8 +26,8 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU diff --git a/src/System.Data.Common/tests/Configurations.props b/src/System.Data.Common/tests/Configurations.props index 1ea25def2d92..4a4fb4632be9 100644 --- a/src/System.Data.Common/tests/Configurations.props +++ b/src/System.Data.Common/tests/Configurations.props @@ -3,7 +3,8 @@ netstandard; - netcoreapp; + netcoreapp-Windows_NT; + netcoreapp-Unix; \ No newline at end of file diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index feff048ef4ec..aef18f7d2d94 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -4,11 +4,14 @@ {B473F77D-4168-4123-932A-E88020B768FA} 0168,0169,0414,0219,0649 + $(DefineConstants);netcoreapp - - + + + + @@ -119,7 +122,7 @@ - + diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs similarity index 100% rename from src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs rename to src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs From 6452411e15d23e972affdf9b015b4df89e55d51d Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 9 Jun 2017 15:20:21 +0200 Subject: [PATCH 08/27] 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 --- src/System.Data.Common/System.Data.Common.sln | 4 +- .../ref/System.Data.Common.cs | 7 +++ .../ref/System.Data.Common.csproj | 3 + .../ref/System.Data.Common.netcoreapp.cs | 18 ++++++ .../src/System.Data.Common.csproj | 2 +- .../System/Data/Common/DbProviderFactories.cs | 56 ++++++++++++------- .../tests/Configurations.props | 3 +- .../tests/System.Data.Common.Tests.csproj | 6 +- 8 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs diff --git a/src/System.Data.Common/System.Data.Common.sln b/src/System.Data.Common/System.Data.Common.sln index 47c84cae391e..aa55793f0971 100644 --- a/src/System.Data.Common/System.Data.Common.sln +++ b/src/System.Data.Common/System.Data.Common.sln @@ -26,8 +26,8 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU diff --git a/src/System.Data.Common/ref/System.Data.Common.cs b/src/System.Data.Common/ref/System.Data.Common.cs index 854d72c0bb4c..a0ec1d683847 100644 --- a/src/System.Data.Common/ref/System.Data.Common.cs +++ b/src/System.Data.Common/ref/System.Data.Common.cs @@ -2211,6 +2211,13 @@ protected DbProviderFactory() { } public virtual System.Data.Common.DbDataSourceEnumerator CreateDataSourceEnumerator() { throw null; } public virtual System.Data.Common.DbParameter CreateParameter() { throw null; } } + public static partial class DbProviderFactories + { + public static DbProviderFactory GetFactory(string providerInvariantName) { throw null; } + public static DbProviderFactory GetFactory(DataRow providerRow) { throw null; } + public static DbProviderFactory GetFactory(DbConnection connection) { throw null; } + public static DataTable GetFactoryClasses() { throw null; } + } [System.AttributeUsageAttribute((System.AttributeTargets)(128), AllowMultiple = false, Inherited = true)] public sealed partial class DbProviderSpecificTypePropertyAttribute : System.Attribute { diff --git a/src/System.Data.Common/ref/System.Data.Common.csproj b/src/System.Data.Common/ref/System.Data.Common.csproj index 0040e706fcf5..977e945e997a 100644 --- a/src/System.Data.Common/ref/System.Data.Common.csproj +++ b/src/System.Data.Common/ref/System.Data.Common.csproj @@ -14,6 +14,9 @@ + + + diff --git a/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs new file mode 100644 index 000000000000..ed5296e55493 --- /dev/null +++ b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs @@ -0,0 +1,18 @@ +// 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. +// ------------------------------------------------------------------------------ + +namespace System.Data.Common +{ + public static partial class DbProviderFactories + { + public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) { throw null; } + public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) { throw null; } + public static void ConfigureFactory(DbConnection connection, string providerInvariantName) { throw null; } + public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) { throw null; } + } +} \ No newline at end of file diff --git a/src/System.Data.Common/src/System.Data.Common.csproj b/src/System.Data.Common/src/System.Data.Common.csproj index 6f92d8ce88a1..fd271778c15d 100644 --- a/src/System.Data.Common/src/System.Data.Common.csproj +++ b/src/System.Data.Common/src/System.Data.Common.csproj @@ -16,7 +16,6 @@ - @@ -213,6 +212,7 @@ + diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 64f341fea72e..252dd475ac95 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -79,43 +79,61 @@ public static DbProviderFactory GetFactory(DataRow providerRow) throw ADP.Argument("The missing .Net Framework Data Provider's assembly qualified name is required."); } + public static DataTable GetFactoryClasses() + { + try + { + _providerTableLock.EnterReadLock(); + return _providerTable == null ? GetInitialProviderTable() : _providerTable.Copy(); + } + finally + { + _providerTableLock.ExitReadLock(); + } + } + public static DbProviderFactory GetFactory(DbConnection connection) { ADP.CheckArgumentNull(connection, nameof(connection)); return connection.ProviderFactory; } - public static void SetFactory(string name="", string providerInvariantName="", string description = "") - where TFactory : DbProviderFactory + public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) { - RegisterFactoryInTable(name, providerInvariantName, typeof(TFactory), description); + ConfigureFactory(providerFactoryClass, providerInvariantName, string.Empty, string.Empty); } - public static void SetFactory(DbConnection connection, string name="", string providerInvariantName = "", string description = "") + public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) { - ADP.CheckArgumentNull(connection, nameof(connection)); - DbProviderFactory factoryInstance = GetFactory(connection); - if (factoryInstance == null) + ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + + if (!typeof(DbProviderFactory).IsAssignableFrom(providerFactoryClass)) { - throw ADP.Argument("The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection"); + throw ADP.Argument($"The type '{providerFactoryClass.FullName}' doesn't inherit from DbProviderFactory"); } - RegisterFactoryInTable(name, providerInvariantName, factoryInstance.GetType(), description); + RegisterFactoryInTable(providerFactoryClass, providerInvariantName, name, description); } - - public static DataTable GetFactoryClasses() + + public static void ConfigureFactory(DbConnection connection, string providerInvariantName) { - try - { - _providerTableLock.EnterReadLock(); - return _providerTable == null ? GetInitialProviderTable() : _providerTable.Copy(); - } - finally + ConfigureFactory(connection, providerInvariantName, string.Empty, string.Empty); + } + + public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) + { + ADP.CheckArgumentNull(connection, nameof(connection)); + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + + DbProviderFactory factoryInstance = GetFactory(connection); + if (factoryInstance == null) { - _providerTableLock.ExitReadLock(); + throw ADP.Argument("The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection."); } + RegisterFactoryInTable(factoryInstance.GetType(), providerInvariantName, name, description); } - private static void RegisterFactoryInTable(string name, string providerInvariantName, Type factoryType, string description) + private static void RegisterFactoryInTable(Type factoryType, string providerInvariantName, string name, string description) { ADP.CheckArgumentNull(factoryType, nameof(factoryType)); string invariantNameToUse = string.IsNullOrWhiteSpace(providerInvariantName) ? factoryType.Namespace : providerInvariantName; diff --git a/src/System.Data.Common/tests/Configurations.props b/src/System.Data.Common/tests/Configurations.props index 4a4fb4632be9..6090260affdf 100644 --- a/src/System.Data.Common/tests/Configurations.props +++ b/src/System.Data.Common/tests/Configurations.props @@ -2,9 +2,8 @@ + netcoreapp; netstandard; - netcoreapp-Windows_NT; - netcoreapp-Unix; \ No newline at end of file diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index aef18f7d2d94..c5718d502d03 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -6,12 +6,10 @@ 0168,0169,0414,0219,0649 $(DefineConstants);netcoreapp + + - - - - From bc5badf54fe879f49baa950c9656f9e9ff5613b4 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 9 Jun 2017 15:59:06 +0200 Subject: [PATCH 09/27] Added tests for DbProviderFactories. --- .../DbProviderFactoriesTests.netcoreapp.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index 98ec8dd058d5..691afe0d8d76 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -14,6 +14,63 @@ public class DbProviderFactoriesTests public void InitializationTest() { DataTable initializedTable = DbProviderFactories.GetFactoryClasses(); + Assert.NotNull(initializedTable); + Assert.Equal(4, initializedTable.Columns.Count); + Assert.Equal("Name", initializedTable.Columns[0].ColumnName); + Assert.Equal("Description", initializedTable.Columns[1].ColumnName); + Assert.Equal("InvariantName", initializedTable.Columns[2].ColumnName); + Assert.Equal("AssemblyQualifiedName", initializedTable.Columns[3].ColumnName); + } + + + [Fact] + [Trait("Issue", "I19826")] + public void GetFactoryEmptyTableTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + } + + + [Fact] + [Trait("Issue", "I19826")] + public void ConfigureFactoryWithTypeTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory), "System.Data.SqlClient"); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + } + + + [Fact] + [Trait("Issue", "I19826")] + public void ConfigureFactoryWithDbConnectionTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection(), "System.Data.SqlClient"); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + } + + + private void ClearDbProviderFactoriesTable() + { + // as the DbProviderFactories table is shared, for tests we need a clean one before a test starts to make sure the tests always succeed. + Type type = typeof(DbProviderFactories); + FieldInfo info = type.GetField("_providerTable", BindingFlags.NonPublic | BindingFlags.Static); + DataTable providerTable = info.GetValue(null) as DataTable; + Assert.NotNull(providerTable); + providerTable.Clear(); + Assert.Equal(0, providerTable.Rows.Count); } } } From 6415bc64833dcf37c324002717caff2753d6302a Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 9 Jun 2017 17:00:23 +0200 Subject: [PATCH 10/27] Better subclass testing added and more tests added --- .../System/Data/Common/DbProviderFactories.cs | 2 +- .../DbProviderFactoriesTests.netcoreapp.cs | 35 ++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 252dd475ac95..39f8d84c286c 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -108,7 +108,7 @@ public static void ConfigureFactory(Type providerFactoryClass, string providerIn ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); - if (!typeof(DbProviderFactory).IsAssignableFrom(providerFactoryClass)) + if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) { throw ADP.Argument($"The type '{providerFactoryClass.FullName}' doesn't inherit from DbProviderFactory"); } diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index 691afe0d8d76..d6069e82df6b 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -7,10 +7,16 @@ namespace System.Data.Common { + public sealed class TestProviderFactory : DbProviderFactory + { + public static readonly TestProviderFactory Instance = new TestProviderFactory(); + private TestProviderFactory() { } + } + public class DbProviderFactoriesTests { + [Fact] - [Trait("Issue", "I19826")] public void InitializationTest() { DataTable initializedTable = DbProviderFactories.GetFactoryClasses(); @@ -22,18 +28,14 @@ public void InitializationTest() Assert.Equal("AssemblyQualifiedName", initializedTable.Columns[3].ColumnName); } - [Fact] - [Trait("Issue", "I19826")] public void GetFactoryEmptyTableTest() { ClearDbProviderFactoriesTable(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); } - [Fact] - [Trait("Issue", "I19826")] public void ConfigureFactoryWithTypeTest() { ClearDbProviderFactoriesTable(); @@ -46,9 +48,7 @@ public void ConfigureFactoryWithTypeTest() Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); } - [Fact] - [Trait("Issue", "I19826")] public void ConfigureFactoryWithDbConnectionTest() { ClearDbProviderFactoriesTable(); @@ -60,8 +60,27 @@ public void ConfigureFactoryWithDbConnectionTest() Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); } + + [Fact] + public void ReplaceFactoryWithConfigureFactoryWithTypeTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory), "System.Data.SqlClient"); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); - + DbProviderFactories.ConfigureFactory(typeof(TestProviderFactory), "System.Data.SqlClient"); + providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(TestProviderFactory), factory.GetType()); + } + private void ClearDbProviderFactoriesTable() { // as the DbProviderFactories table is shared, for tests we need a clean one before a test starts to make sure the tests always succeed. From 9473284d70cc7a9fb72b640e2cf7e2cdcc7b999c Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 9 Jun 2017 19:26:34 +0200 Subject: [PATCH 11/27] Moved all hard-coded strings to SR/Strings.resx. Added test for factory retrieval using a connection --- src/System.Data.Common/src/Resources/Strings.resx | 6 ++++++ .../src/System/Data/Common/DbProviderFactories.cs | 12 ++++++------ .../Common/DbProviderFactoriesTests.netcoreapp.cs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/System.Data.Common/src/Resources/Strings.resx b/src/System.Data.Common/src/Resources/Strings.resx index 8ac6107e7fc7..698a74bbe867 100644 --- a/src/System.Data.Common/src/Resources/Strings.resx +++ b/src/System.Data.Common/src/Resources/Strings.resx @@ -512,4 +512,10 @@ Cannot remove this column, because it is part of an expression: {0} = {1}. The rowOrder value={0} has been found twice for table named '{1}'. Cannot find ElementType name='{0}'. + The specified invariant name '{0}' wasn't found in the list of registered .Net Framework Data Providers. + The requested .Net Framework Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type. + The registered .Net Framework Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded. + The missing .Net Framework Data Provider's assembly qualified name is required. + The type '{0}' doesn't inherit from DbProviderFactory. + The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection. diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 39f8d84c286c..6c4c7ce7b7cf 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -35,7 +35,7 @@ public static DbProviderFactory GetFactory(string providerInvariantName) return DbProviderFactories.GetFactory(providerRow); } } - throw ADP.Argument($"The specified invariant name '{providerInvariantName}' wasn't found in the list of registered .Net Framework Data Providers.", providerInvariantName); + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)); } finally { @@ -71,12 +71,12 @@ public static DbProviderFactory GetFactory(DataRow providerRow) } // else throw DataProviderInvalid } - throw ADP.InvalidOperation("The requested .Net Framework Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type."); + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); } - throw ADP.Argument($"The registered .Net Framework Data Provider's DbProviderFactory implementation type '{assemblyQualifiedName}' couldn't be loaded."); + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); } } - throw ADP.Argument("The missing .Net Framework Data Provider's assembly qualified name is required."); + throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); } public static DataTable GetFactoryClasses() @@ -110,7 +110,7 @@ public static void ConfigureFactory(Type providerFactoryClass, string providerIn if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) { - throw ADP.Argument($"The type '{providerFactoryClass.FullName}' doesn't inherit from DbProviderFactory"); + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); } RegisterFactoryInTable(providerFactoryClass, providerInvariantName, name, description); } @@ -128,7 +128,7 @@ public static void ConfigureFactory(DbConnection connection, string providerInva DbProviderFactory factoryInstance = GetFactory(connection); if (factoryInstance == null) { - throw ADP.Argument("The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection."); + throw ADP.Argument(SR.ADP_DbProviderFactories_NoFactorySuppliedThroughDbConnection); } RegisterFactoryInTable(factoryInstance.GetType(), providerInvariantName, name, description); } diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index d6069e82df6b..b59c62e6aa65 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -60,6 +60,19 @@ public void ConfigureFactoryWithDbConnectionTest() Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); } + + [Fact] + public void GetFactoryWithDbConnectionTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection(), "System.Data.SqlClient"); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory(new System.Data.SqlClient.SqlConnection()); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + } [Fact] public void ReplaceFactoryWithConfigureFactoryWithTypeTest() From b7d78cf2d38100656a3ada9b65a124c761e29459 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 10 Jun 2017 10:41:46 +0200 Subject: [PATCH 12/27] Removal of now redundant comment. See: https://github.com/dotnet/corefx/pull/19885#pullrequestreview-43266721 --- src/System.Data.Common/src/System/Data/Common/DbConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs index 12a7deb023f6..af7ebd8ed50d 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbConnection.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbConnection.cs @@ -35,7 +35,7 @@ protected DbConnection() : base() /// protected virtual DbProviderFactory DbProviderFactory => null; - internal DbProviderFactory ProviderFactory => DbProviderFactory; // This property is required for DbProviderFactories' API and will appear as 'dead' code till that class has been ported. + internal DbProviderFactory ProviderFactory => DbProviderFactory; [Browsable(false)] public abstract string ServerVersion { get; } From 920a0baa4b2a3d6f19ab8d473854e4344e64008f Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 10 Jun 2017 10:45:02 +0200 Subject: [PATCH 13/27] Comment correction for bad English --- .../src/System/Data/Common/DbProviderFactories.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 6c4c7ce7b7cf..d9a44df7849d 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -172,7 +172,7 @@ private static DataTable GetInitialProviderTable() descriptionColumn.ReadOnly = true; DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)); invariantNameColumn.ReadOnly = true; - // This column isn't readonly in CoreFx, while it is readonly in NetFx, as this class allow overwriting registered providers at runtime. + // This column is writable in CoreFx, while it is readonly in NetFx, as this class allows overwriting registered providers at runtime. DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)); DataColumn[] primaryKey = new DataColumn[] { invariantNameColumn }; From c37583e6da23be30afbbdefe6ebbd158a65f938f Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 10 Jun 2017 11:07:09 +0200 Subject: [PATCH 14/27] Refactored API a bit, see: https://github.com/dotnet/standard/issues/356#issuecomment-307552750 --- .../ref/System.Data.Common.netcoreapp.cs | 2 ++ .../System/Data/Common/DbProviderFactories.cs | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs index ed5296e55493..b18c29d4ba49 100644 --- a/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs +++ b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs @@ -10,8 +10,10 @@ namespace System.Data.Common { public static partial class DbProviderFactories { + public static void ConfigureFactory(Type providerFactoryClass) { throw null; } public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) { throw null; } public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) { throw null; } + public static void ConfigureFactory(DbConnection connection) { throw null; } public static void ConfigureFactory(DbConnection connection, string providerInvariantName) { throw null; } public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) { throw null; } } diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index d9a44df7849d..e48f24d582e2 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -97,6 +97,11 @@ public static DbProviderFactory GetFactory(DbConnection connection) ADP.CheckArgumentNull(connection, nameof(connection)); return connection.ProviderFactory; } + + public static void ConfigureFactory(Type providerFactoryClass) + { + ConfigureFactory(providerFactoryClass, string.Empty, string.Empty, string.Empty); + } public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) { @@ -106,7 +111,6 @@ public static void ConfigureFactory(Type providerFactoryClass, string providerIn public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) { ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); - ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) { @@ -114,7 +118,12 @@ public static void ConfigureFactory(Type providerFactoryClass, string providerIn } RegisterFactoryInTable(providerFactoryClass, providerInvariantName, name, description); } - + + public static void ConfigureFactory(DbConnection connection) + { + ConfigureFactory(connection, string.Empty, string.Empty, string.Empty); + } + public static void ConfigureFactory(DbConnection connection, string providerInvariantName) { ConfigureFactory(connection, providerInvariantName, string.Empty, string.Empty); @@ -123,7 +132,6 @@ public static void ConfigureFactory(DbConnection connection, string providerInva public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) { ADP.CheckArgumentNull(connection, nameof(connection)); - ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); DbProviderFactory factoryInstance = GetFactory(connection); if (factoryInstance == null) @@ -136,8 +144,8 @@ public static void ConfigureFactory(DbConnection connection, string providerInva private static void RegisterFactoryInTable(Type factoryType, string providerInvariantName, string name, string description) { ADP.CheckArgumentNull(factoryType, nameof(factoryType)); - string invariantNameToUse = string.IsNullOrWhiteSpace(providerInvariantName) ? factoryType.Namespace : providerInvariantName; + string invariantNameToUse = string.IsNullOrWhiteSpace(providerInvariantName) ? factoryType.Namespace : providerInvariantName; try { _providerTableLock.EnterWriteLock(); From 9168ac99b77a7fc00a47adeb3c596ddce5935505 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 10 Jun 2017 11:12:59 +0200 Subject: [PATCH 15/27] Added tests for reworked API. Added tests for wrong type passing --- .../DbProviderFactoriesTests.netcoreapp.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index b59c62e6aa65..269cf0a8e5e8 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -15,7 +15,6 @@ private TestProviderFactory() { } public class DbProviderFactoriesTests { - [Fact] public void InitializationTest() { @@ -47,7 +46,28 @@ public void ConfigureFactoryWithTypeTest() Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); } - + + [Fact] + public void ConfigureFactoryWithTypeNoNameSpecifiedTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory)); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + } + + [Fact] + public void ConfigureFactoryWithWrongTypeTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + Assert.Throws(() => DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlConnection))); + } + [Fact] public void ConfigureFactoryWithDbConnectionTest() { @@ -61,6 +81,19 @@ public void ConfigureFactoryWithDbConnectionTest() Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); } + [Fact] + public void ConfigureFactoryWithDbConnectionNoNameSpecifiedTest() + { + ClearDbProviderFactoriesTable(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection()); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + } + [Fact] public void GetFactoryWithDbConnectionTest() { From bc836c574da306144e0679b58a1bacd1cca7f557 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 10 Jun 2017 11:15:46 +0200 Subject: [PATCH 16/27] Reverting change in sln file: See https://github.com/dotnet/corefx/pull/19885#pullrequestreview-43266364 --- src/System.Data.Common/System.Data.Common.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Data.Common/System.Data.Common.sln b/src/System.Data.Common/System.Data.Common.sln index aa55793f0971..816b5fd8bf29 100644 --- a/src/System.Data.Common/System.Data.Common.sln +++ b/src/System.Data.Common/System.Data.Common.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.12 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.Common.Tests", "tests\System.Data.Common.Tests.csproj", "{B473F77D-4168-4123-932A-E88020B768FA}" ProjectSection(ProjectDependencies) = postProject From f84c3a825cf0722a8a726ca509da519e5ed2ef55 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 10 Nov 2017 10:41:42 +0100 Subject: [PATCH 17/27] Almost done implementation using DataTable internal storage. Refactoring now to concurrentdictionary --- .../src/Resources/Strings.resx | 1 - .../System/Data/Common/DbProviderFactories.cs | 196 ++++++++++-------- .../DbProviderFactoriesTests.netcoreapp.cs | 6 +- 3 files changed, 116 insertions(+), 87 deletions(-) diff --git a/src/System.Data.Common/src/Resources/Strings.resx b/src/System.Data.Common/src/Resources/Strings.resx index 698a74bbe867..a0cee9a5519e 100644 --- a/src/System.Data.Common/src/Resources/Strings.resx +++ b/src/System.Data.Common/src/Resources/Strings.resx @@ -517,5 +517,4 @@ The registered .Net Framework Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded. The missing .Net Framework Data Provider's assembly qualified name is required. The type '{0}' doesn't inherit from DbProviderFactory. - The .Net Framework Data Provider doesn't supply a DbProviderFactory implementation via DbConnection. diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index e48f24d582e2..5f4df2c25dab 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -19,28 +19,18 @@ public static partial class DbProviderFactories private const string Instance = "Instance"; private static DataTable _providerTable = GetInitialProviderTable(); - private static ReaderWriterLockSlim _providerTableLock = new ReaderWriterLockSlim(); + private static readonly ReaderWriterLockSlim _providerTableLock = new ReaderWriterLockSlim(); + + + public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) + { + factory = DbProviderFactories.GetFactory(providerInvariantName, throwOnError: false); + return factory != null; + } public static DbProviderFactory GetFactory(string providerInvariantName) { - ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); - try - { - _providerTableLock.EnterReadLock(); - if (null != _providerTable) - { - DataRow providerRow = _providerTable.Rows.Find(providerInvariantName); - if (null != providerRow) - { - return DbProviderFactories.GetFactory(providerRow); - } - } - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)); - } - finally - { - _providerTableLock.ExitReadLock(); - } + return DbProviderFactories.GetFactory(providerInvariantName, throwOnError:true); } public static DbProviderFactory GetFactory(DataRow providerRow) @@ -48,35 +38,43 @@ public static DbProviderFactory GetFactory(DataRow providerRow) ADP.CheckArgumentNull(providerRow, nameof(providerRow)); // no need for locking on the table, as the row can only be from a DataTable that's a copy of the one contained in this class. DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName]; - if (null != assemblyQualifiedNameColumn) + if (null == assemblyQualifiedNameColumn) { - // column value may not be a string - string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; - if (!string.IsNullOrWhiteSpace(assemblyQualifiedName)) - { - Type providerType = Type.GetType(assemblyQualifiedName); - if (null != providerType) - { - System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); - if (null != providerInstance) - { - if (providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) - { - object factory = providerInstance.GetValue(null); - if (null != factory) - { - return (DbProviderFactory)factory; - } - // else throw DataProviderInvalid - } - // else throw DataProviderInvalid - } - throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); - } - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); - } + throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); } - throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); + + string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; + if (string.IsNullOrWhiteSpace(assemblyQualifiedName)) + { + throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); + } + Type providerType = Type.GetType(assemblyQualifiedName); + if (null == providerType) + { + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); + } + System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | + System.Reflection.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; + } + + public static DbProviderFactory GetFactory(DbConnection connection) + { + ADP.CheckArgumentNull(connection, nameof(connection)); + return connection.ProviderFactory; } public static DataTable GetFactoryClasses() @@ -92,56 +90,90 @@ public static DataTable GetFactoryClasses() } } - public static DbProviderFactory GetFactory(DbConnection connection) - { - ADP.CheckArgumentNull(connection, nameof(connection)); - return connection.ProviderFactory; - } - - public static void ConfigureFactory(Type providerFactoryClass) + public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) { - ConfigureFactory(providerFactoryClass, string.Empty, string.Empty, string.Empty); +#warning TODO } - public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) - { - ConfigureFactory(providerFactoryClass, providerInvariantName, string.Empty, string.Empty); - } - public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) + public static void RegisterFactory(string providerInvariantName, Type providerFactoryClass) { ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); - if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) { throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); } - RegisterFactoryInTable(providerFactoryClass, providerInvariantName, name, description); + DbProviderFactories.RegisterFactoryInTable(providerInvariantName, providerFactoryClass); } - public static void ConfigureFactory(DbConnection connection) + public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory) { - ConfigureFactory(connection, string.Empty, string.Empty, string.Empty); + ADP.CheckArgumentNull(factory, nameof(factory)); + DbProviderFactories.RegisterFactoryInTable(providerInvariantName, factory.GetType()); } - - public static void ConfigureFactory(DbConnection connection, string providerInvariantName) + + public static bool UnregisterFactory(string providerInvariantName) { - ConfigureFactory(connection, providerInvariantName, string.Empty, string.Empty); + if (string.IsNullOrWhiteSpace(providerInvariantName)) + { + return false; + } + try + { + _providerTableLock.EnterReadLock(); + DataRow providerRow = GetProviderRowFromTable(providerInvariantName, throwOnError: false); + if (null == providerRow) + { + return false; + } + _providerTable.Rows.Remove(providerRow); + return true; + } + finally + { + _providerTableLock.ExitReadLock(); + } } - public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) + private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) { - ADP.CheckArgumentNull(connection, nameof(connection)); + if (throwOnError) + { + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + } + else + { + if (string.IsNullOrWhiteSpace(providerInvariantName)) + { + return null; + } + } + try + { + _providerTableLock.EnterReadLock(); + DataRow providerRow = GetProviderRowFromTable(providerInvariantName, throwOnError); + if (null == providerRow) + { + return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; + } + return DbProviderFactories.GetFactory(providerRow); + } + finally + { + _providerTableLock.ExitReadLock(); + } + } - DbProviderFactory factoryInstance = GetFactory(connection); - if (factoryInstance == null) + private static DataRow GetProviderRowFromTable(string providerInvariantName, bool throwOnError) + { + if (null == _providerTable) { - throw ADP.Argument(SR.ADP_DbProviderFactories_NoFactorySuppliedThroughDbConnection); + return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DataRow)null; } - RegisterFactoryInTable(factoryInstance.GetType(), providerInvariantName, name, description); + return _providerTable.Rows.Find(providerInvariantName); } - private static void RegisterFactoryInTable(Type factoryType, string providerInvariantName, string name, string description) + private static void RegisterFactoryInTable(string providerInvariantName, Type factoryType) { ADP.CheckArgumentNull(factoryType, nameof(factoryType)); @@ -156,9 +188,9 @@ private static void RegisterFactoryInTable(Type factoryType, string providerInva { newRow = true; rowToAlter = _providerTable.NewRow(); - rowToAlter[Name] = string.IsNullOrWhiteSpace(name) ? invariantNameToUse : name; + rowToAlter[Name] = invariantNameToUse; rowToAlter[InvariantName] = invariantNameToUse; - rowToAlter[Description] = description ?? string.Empty; + rowToAlter[Description] = string.Empty; } rowToAlter[AssemblyQualifiedName] = factoryType.AssemblyQualifiedName; if (newRow) @@ -172,21 +204,19 @@ private static void RegisterFactoryInTable(Type factoryType, string providerInva } } + private static DataTable GetInitialProviderTable() { - DataColumn nameColumn = new DataColumn(Name, typeof(string)); - nameColumn.ReadOnly = true; - DataColumn descriptionColumn = new DataColumn(Description, typeof(string)); - descriptionColumn.ReadOnly = true; - DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)); - invariantNameColumn.ReadOnly = true; + DataColumn nameColumn = new DataColumn(Name, typeof(string)) { ReadOnly = true }; + DataColumn descriptionColumn = new DataColumn(Description, typeof(string)) { ReadOnly = true }; + DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)) { ReadOnly = true }; + // This column is writable in CoreFx, while it is readonly in NetFx, as this class allows overwriting registered providers at runtime. DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)); DataColumn[] primaryKey = new DataColumn[] { invariantNameColumn }; DataColumn[] columns = new DataColumn[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }; - DataTable initialTable = new DataTable(ProviderGroup); - initialTable.Locale = CultureInfo.InvariantCulture; + DataTable initialTable = new DataTable(ProviderGroup) { Locale = CultureInfo.InvariantCulture }; initialTable.Columns.AddRange(columns); initialTable.PrimaryKey = primaryKey; return initialTable; diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index 269cf0a8e5e8..d43501083556 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -39,7 +39,7 @@ public void ConfigureFactoryWithTypeTest() { ClearDbProviderFactoriesTable(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory), "System.Data.SqlClient"); + DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(System.Data.SqlClient.SqlClientFactory)); DataTable providerTable = DbProviderFactories.GetFactoryClasses(); Assert.Equal(1, providerTable.Rows.Count); DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); @@ -112,14 +112,14 @@ public void ReplaceFactoryWithConfigureFactoryWithTypeTest() { ClearDbProviderFactoriesTable(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory), "System.Data.SqlClient"); + DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(System.Data.SqlClient.SqlClientFactory)); DataTable providerTable = DbProviderFactories.GetFactoryClasses(); Assert.Equal(1, providerTable.Rows.Count); DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); - DbProviderFactories.ConfigureFactory(typeof(TestProviderFactory), "System.Data.SqlClient"); + DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(TestProviderFactory)); providerTable = DbProviderFactories.GetFactoryClasses(); Assert.Equal(1, providerTable.Rows.Count); factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); From 356f39312f208d9fdd1b865c480b98b02502ad84 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 10 Nov 2017 14:25:56 +0100 Subject: [PATCH 18/27] Final work for #20903 --- .../ref/System.Data.Common.netcoreapp.cs | 16 +- .../System/Data/Common/DbProviderFactories.cs | 175 ++++++------------ .../DbProviderFactoriesTests.netcoreapp.cs | 162 ++++++++++------ 3 files changed, 170 insertions(+), 183 deletions(-) diff --git a/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs index b18c29d4ba49..94df2006ad59 100644 --- a/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs +++ b/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs @@ -6,15 +6,17 @@ // 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 ConfigureFactory(Type providerFactoryClass) { throw null; } - public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName) { throw null; } - public static void ConfigureFactory(Type providerFactoryClass, string providerInvariantName, string name, string description) { throw null; } - public static void ConfigureFactory(DbConnection connection) { throw null; } - public static void ConfigureFactory(DbConnection connection, string providerInvariantName) { throw null; } - public static void ConfigureFactory(DbConnection connection, string providerInvariantName, string name, string description) { throw null; } + 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 GetProviderInvariantNames() { throw null; } } -} \ No newline at end of file +} diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 5f4df2c25dab..b925b6e73290 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -3,8 +3,11 @@ // 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.Threading; namespace System.Data.Common @@ -18,8 +21,7 @@ public static partial class DbProviderFactories private const string ProviderGroup = "DbProviderFactories"; private const string Instance = "Instance"; - private static DataTable _providerTable = GetInitialProviderTable(); - private static readonly ReaderWriterLockSlim _providerTableLock = new ReaderWriterLockSlim(); + private static ConcurrentDictionary _registeredFactories = new ConcurrentDictionary(); public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) @@ -36,13 +38,11 @@ public static DbProviderFactory GetFactory(string providerInvariantName) public static DbProviderFactory GetFactory(DataRow providerRow) { ADP.CheckArgumentNull(providerRow, nameof(providerRow)); - // no need for locking on the table, as the row can only be from a DataTable that's a copy of the one contained in this class. DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName]; if (null == assemblyQualifiedNameColumn) { throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); } - string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; if (string.IsNullOrWhiteSpace(assemblyQualifiedName)) { @@ -53,22 +53,7 @@ public static DbProviderFactory GetFactory(DataRow providerRow) { throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); } - System.Reflection.FieldInfo providerInstance = providerType.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | - System.Reflection.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; + return GetFactoryInstance(providerType); } public static DbProviderFactory GetFactory(DbConnection connection) @@ -79,60 +64,58 @@ public static DbProviderFactory GetFactory(DbConnection connection) public static DataTable GetFactoryClasses() { - try - { - _providerTableLock.EnterReadLock(); - return _providerTable == null ? GetInitialProviderTable() : _providerTable.Copy(); - } - finally + DataColumn nameColumn = new DataColumn(Name, typeof(string)) { ReadOnly = true }; + DataColumn descriptionColumn = new DataColumn(Description, typeof(string)) { ReadOnly = true }; + DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)) { ReadOnly = true }; + DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)) { ReadOnly = true}; + + DataTable toReturn = new DataTable(ProviderGroup) { Locale = CultureInfo.InvariantCulture }; + toReturn.Columns.AddRange(new[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }); + toReturn.PrimaryKey = new[] { invariantNameColumn }; + foreach(var kvp in _registeredFactories) { - _providerTableLock.ExitReadLock(); + DataRow newRow = toReturn.NewRow(); + newRow[InvariantName] = kvp.Key; + newRow[AssemblyQualifiedName] = kvp.Value.GetType().AssemblyQualifiedName; + newRow[Name] = string.Empty; + newRow[Description] = string.Empty; + toReturn.AddRow(newRow); } + return toReturn; } - public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) + public static IEnumerable GetProviderInvariantNames() { -#warning TODO + return _registeredFactories.Keys.ToList(); } - - public static void RegisterFactory(string providerInvariantName, Type providerFactoryClass) + public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) { - ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); - if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(providerInvariantName)); + Type providerType = Type.GetType(factoryTypeAssemblyQualifiedName); + if (null == providerType) { - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, factoryTypeAssemblyQualifiedName)); } - DbProviderFactories.RegisterFactoryInTable(providerInvariantName, providerFactoryClass); + RegisterFactory(providerInvariantName, providerType); + } + + 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)); - DbProviderFactories.RegisterFactoryInTable(providerInvariantName, factory.GetType()); + _registeredFactories[providerInvariantName] = factory; } public static bool UnregisterFactory(string providerInvariantName) { - if (string.IsNullOrWhiteSpace(providerInvariantName)) - { - return false; - } - try - { - _providerTableLock.EnterReadLock(); - DataRow providerRow = GetProviderRowFromTable(providerInvariantName, throwOnError: false); - if (null == providerRow) - { - return false; - } - _providerTable.Rows.Remove(providerRow); - return true; - } - finally - { - _providerTableLock.ExitReadLock(); - } + return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out DbProviderFactory registeredFactory); } private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) @@ -148,78 +131,38 @@ private static DbProviderFactory GetFactory(string providerInvariantName, bool t return null; } } - try - { - _providerTableLock.EnterReadLock(); - DataRow providerRow = GetProviderRowFromTable(providerInvariantName, throwOnError); - if (null == providerRow) - { - return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; - } - return DbProviderFactories.GetFactory(providerRow); - } - finally + bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out DbProviderFactory registeredFactory); + if (!wasRegistered) { - _providerTableLock.ExitReadLock(); + return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; } + return registeredFactory; } - - private static DataRow GetProviderRowFromTable(string providerInvariantName, bool throwOnError) + + private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) { - if (null == _providerTable) + ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); + if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) { - return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DataRow)null; + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); } - return _providerTable.Rows.Find(providerInvariantName); - } - - private static void RegisterFactoryInTable(string providerInvariantName, Type factoryType) - { - ADP.CheckArgumentNull(factoryType, nameof(factoryType)); - string invariantNameToUse = string.IsNullOrWhiteSpace(providerInvariantName) ? factoryType.Namespace : providerInvariantName; - try + System.Reflection.FieldInfo providerInstance = providerFactoryClass.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static); + if (null == providerInstance) { - _providerTableLock.EnterWriteLock(); - - bool newRow = false; - DataRow rowToAlter = _providerTable.Rows.Find(invariantNameToUse); - if (rowToAlter == null) - { - newRow = true; - rowToAlter = _providerTable.NewRow(); - rowToAlter[Name] = invariantNameToUse; - rowToAlter[InvariantName] = invariantNameToUse; - rowToAlter[Description] = string.Empty; - } - rowToAlter[AssemblyQualifiedName] = factoryType.AssemblyQualifiedName; - if (newRow) - { - _providerTable.AddRow(rowToAlter); - } + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); } - finally + if (!providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) { - _providerTableLock.ExitWriteLock(); + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); } - } - - - private static DataTable GetInitialProviderTable() - { - DataColumn nameColumn = new DataColumn(Name, typeof(string)) { ReadOnly = true }; - DataColumn descriptionColumn = new DataColumn(Description, typeof(string)) { ReadOnly = true }; - DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)) { ReadOnly = true }; - - // This column is writable in CoreFx, while it is readonly in NetFx, as this class allows overwriting registered providers at runtime. - DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)); - - DataColumn[] primaryKey = new DataColumn[] { invariantNameColumn }; - DataColumn[] columns = new DataColumn[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }; - DataTable initialTable = new DataTable(ProviderGroup) { Locale = CultureInfo.InvariantCulture }; - initialTable.Columns.AddRange(columns); - initialTable.PrimaryKey = primaryKey; - return initialTable; + object factory = providerInstance.GetValue(null); + if (null == factory) + { + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); + } + return (DbProviderFactory)factory; } } } diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index d43501083556..9c4dd54a9167 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Reflection; using System.Data.Common; +using System.Linq; using Xunit; namespace System.Data.Common @@ -16,7 +19,7 @@ private TestProviderFactory() { } public class DbProviderFactoriesTests { [Fact] - public void InitializationTest() + public void GetFactoryClassesDataTableShapeTest() { DataTable initializedTable = DbProviderFactories.GetFactoryClasses(); Assert.NotNull(initializedTable); @@ -28,114 +31,153 @@ public void InitializationTest() } [Fact] - public void GetFactoryEmptyTableTest() + public void GetFactoryNoRegistrationTest() { - ClearDbProviderFactoriesTable(); + ClearRegisteredFactories(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); } [Fact] - public void ConfigureFactoryWithTypeTest() + public void GetFactoryWithInvariantNameTest() { - ClearDbProviderFactoriesTable(); - Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(System.Data.SqlClient.SqlClientFactory)); - DataTable providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); } [Fact] - public void ConfigureFactoryWithTypeNoNameSpecifiedTest() + public void GetFactoryWithDbConnectionTest() { - ClearDbProviderFactoriesTable(); - Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlClientFactory)); - DataTable providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); - DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactory factory = DbProviderFactories.GetFactory(new System.Data.SqlClient.SqlConnection()); Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); } [Fact] - public void ConfigureFactoryWithWrongTypeTest() + public void GetFactoryWithDataRowTest() { - ClearDbProviderFactoriesTable(); - Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - Assert.Throws(() => DbProviderFactories.ConfigureFactory(typeof(System.Data.SqlClient.SqlConnection))); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=> DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); } [Fact] - public void ConfigureFactoryWithDbConnectionTest() + public void RegisterFactoryWithTypeNameTest() { - ClearDbProviderFactoriesTable(); - Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection(), "System.Data.SqlClient"); - DataTable providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); - DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); - Assert.NotNull(factory); - Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory).AssemblyQualifiedName)); } [Fact] - public void ConfigureFactoryWithDbConnectionNoNameSpecifiedTest() + public void RegisterFactoryWithTypeTest() { - ClearDbProviderFactoriesTable(); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + } + + [Fact] + public void RegisterFactoryWithInstanceTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + } + + [Fact] + public void RegisterFactoryWithWrongTypeTest() + { + ClearRegisteredFactories(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection()); - DataTable providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); - DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); - Assert.NotNull(factory); - Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Throws(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlConnection))); } [Fact] - public void GetFactoryWithDbConnectionTest() + public void RegisterFactoryWithBadInvariantNameTest() { - ClearDbProviderFactoriesTable(); + ClearRegisteredFactories(); Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory(new System.Data.SqlClient.SqlConnection(), "System.Data.SqlClient"); + Assert.Throws(() => DbProviderFactories.RegisterFactory(string.Empty, typeof(System.Data.SqlClient.SqlClientFactory))); + } + + [Fact] + public void UnregisterFactoryTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + Assert.True(DbProviderFactories.UnregisterFactory("System.Data.SqlClient")); DataTable providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); - DbProviderFactory factory = DbProviderFactories.GetFactory(new System.Data.SqlClient.SqlConnection()); + Assert.Equal(0, providerTable.Rows.Count); + } + + [Fact] + public void TryGetFactoryTest() + { + ClearRegisteredFactories(); + Assert.False(DbProviderFactories.TryGetFactory("System.Data.SqlClient", out DbProviderFactory f)); + RegisterSqlClientAndTestRegistration(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + Assert.True(DbProviderFactories.TryGetFactory("System.Data.SqlClient", out DbProviderFactory factory)); Assert.NotNull(factory); Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); } - + [Fact] - public void ReplaceFactoryWithConfigureFactoryWithTypeTest() + public void ReplaceFactoryWithRegisterFactoryWithTypeTest() { - ClearDbProviderFactoriesTable(); - Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); - DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(System.Data.SqlClient.SqlClientFactory)); + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(TestProviderFactory)); DataTable providerTable = DbProviderFactories.GetFactoryClasses(); Assert.Equal(1, providerTable.Rows.Count); DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); Assert.NotNull(factory); - Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); - - DbProviderFactories.ConfigureFactory((Type)"System.Data.SqlClient", (string)typeof(TestProviderFactory)); - providerTable = DbProviderFactories.GetFactoryClasses(); - Assert.Equal(1, providerTable.Rows.Count); - factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); - Assert.NotNull(factory); Assert.Equal(typeof(TestProviderFactory), factory.GetType()); + Assert.Equal(TestProviderFactory.Instance, factory); } - - private void ClearDbProviderFactoriesTable() + + [Fact] + public void GetProviderInvariantNamesTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactories.RegisterFactory("System.Data.Common.TestProvider", typeof(TestProviderFactory)); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(2, providerTable.Rows.Count); + List invariantNames = DbProviderFactories.GetProviderInvariantNames().ToList(); + Assert.Equal(invariantNames.Count, 2); + Assert.True(invariantNames.Contains("System.Data.Common.TestProvider")); + Assert.True(invariantNames.Contains("System.Data.SqlClient")); + } + + private void ClearRegisteredFactories() { // as the DbProviderFactories table is shared, for tests we need a clean one before a test starts to make sure the tests always succeed. Type type = typeof(DbProviderFactories); - FieldInfo info = type.GetField("_providerTable", BindingFlags.NonPublic | BindingFlags.Static); - DataTable providerTable = info.GetValue(null) as DataTable; - Assert.NotNull(providerTable); - providerTable.Clear(); + FieldInfo info = type.GetField("_registeredFactories", BindingFlags.NonPublic | BindingFlags.Static); + ConcurrentDictionary providerStorage = info.GetValue(null) as ConcurrentDictionary; + Assert.NotNull(providerStorage); + providerStorage.Clear(); + Assert.Equal(0, providerStorage.Count); + } + + + private void RegisterSqlClientAndTestRegistration(Action registrationFunc) + { + Assert.NotNull(registrationFunc); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); Assert.Equal(0, providerTable.Rows.Count); + registrationFunc(); + providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory(providerTable.Rows[0]); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); } } } From bf607f2f1deeaddea3f22063ce1b176d6d811777 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Thu, 16 Nov 2017 14:21:14 +0100 Subject: [PATCH 19/27] 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) --- .../System/Data/Common/DbProviderFactories.cs | 65 ++++++++++++++----- .../DbProviderFactoriesTests.netcoreapp.cs | 27 +++++++- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index b925b6e73290..c2b85b59773e 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -14,6 +14,24 @@ namespace System.Data.Common { public static partial class DbProviderFactories { + private struct ProviderRegistration + { + internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProviderFactory factoryInstance) + { + ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName)); + this.FactoryTypeAssemblyQualifiedName = factoryTypeAssemblyQualifiedName; + this.FactoryInstance = factoryInstance; + } + + internal string FactoryTypeAssemblyQualifiedName { get; } + /// + /// The cached instance of the type in . If null, this registation is seen as a deferred registration + /// and is checked the first time when this registration is requested through GetFactory(). + /// + internal DbProviderFactory FactoryInstance { get; } + } + + private static ConcurrentDictionary _registeredFactories = new ConcurrentDictionary(); private const string AssemblyQualifiedName = "AssemblyQualifiedName"; private const string InvariantName = "InvariantName"; private const string Name = "Name"; @@ -21,33 +39,33 @@ public static partial class DbProviderFactories private const string ProviderGroup = "DbProviderFactories"; private const string Instance = "Instance"; - private static ConcurrentDictionary _registeredFactories = new ConcurrentDictionary(); - - public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) { - factory = DbProviderFactories.GetFactory(providerInvariantName, throwOnError: false); + factory = GetFactory(providerInvariantName, throwOnError: false); return factory != null; } public static DbProviderFactory GetFactory(string providerInvariantName) { - return DbProviderFactories.GetFactory(providerInvariantName, throwOnError:true); + return GetFactory(providerInvariantName, throwOnError:true); } public static DbProviderFactory GetFactory(DataRow providerRow) { ADP.CheckArgumentNull(providerRow, nameof(providerRow)); + DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName]; 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); } + Type providerType = Type.GetType(assemblyQualifiedName); if (null == providerType) { @@ -59,6 +77,7 @@ public static DbProviderFactory GetFactory(DataRow providerRow) public static DbProviderFactory GetFactory(DbConnection connection) { ADP.CheckArgumentNull(connection, nameof(connection)); + return connection.ProviderFactory; } @@ -76,7 +95,7 @@ public static DataTable GetFactoryClasses() { DataRow newRow = toReturn.NewRow(); newRow[InvariantName] = kvp.Key; - newRow[AssemblyQualifiedName] = kvp.Value.GetType().AssemblyQualifiedName; + newRow[AssemblyQualifiedName] = kvp.Value.FactoryTypeAssemblyQualifiedName; newRow[Name] = string.Empty; newRow[Description] = string.Empty; toReturn.AddRow(newRow); @@ -92,13 +111,10 @@ public static IEnumerable GetProviderInvariantNames() public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) { ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); - ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(providerInvariantName)); - Type providerType = Type.GetType(factoryTypeAssemblyQualifiedName); - if (null == providerType) - { - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, factoryTypeAssemblyQualifiedName)); - } - RegisterFactory(providerInvariantName, providerType); + 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) @@ -110,12 +126,13 @@ public static void RegisterFactory(string providerInvariantName, DbProviderFacto { ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); ADP.CheckArgumentNull(factory, nameof(factory)); - _registeredFactories[providerInvariantName] = factory; + + _registeredFactories[providerInvariantName] = new ProviderRegistration(factory.GetType().AssemblyQualifiedName, factory); } public static bool UnregisterFactory(string providerInvariantName) { - return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out DbProviderFactory registeredFactory); + return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out ProviderRegistration providerRegistration); } private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) @@ -131,12 +148,26 @@ private static DbProviderFactory GetFactory(string providerInvariantName, bool t return null; } } - bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out DbProviderFactory registeredFactory); + bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out ProviderRegistration registration); if (!wasRegistered) { return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; } - return registeredFactory; + 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. + Type providerType = Type.GetType(registration.FactoryTypeAssemblyQualifiedName); + if (null == providerType) + { + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, registration.FactoryTypeAssemblyQualifiedName)); + } + toReturn = GetFactoryInstance(providerType); + RegisterFactory(providerInvariantName, toReturn); + } + return toReturn; } private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index 9c4dd54a9167..19cf1b4c77ad 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // See the LICENSE file in the project root for more information. +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; @@ -16,6 +17,7 @@ public sealed class TestProviderFactory : DbProviderFactory private TestProviderFactory() { } } + [Trait("Tests", "DbProviderFactories")] public class DbProviderFactoriesTests { [Fact] @@ -102,6 +104,29 @@ public void RegisterFactoryWithBadInvariantNameTest() Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); Assert.Throws(() => DbProviderFactories.RegisterFactory(string.Empty, typeof(System.Data.SqlClient.SqlClientFactory))); } + + [Fact] + public void RegisterFactoryWithAssemblyQualifiedNameTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory).AssemblyQualifiedName)); + } + + [Fact] + public void RegisterFactoryWithWrongAssemblyQualifiedNameTest() + { + ClearRegisteredFactories(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(0, providerTable.Rows.Count); + // register the connection type which is the wrong type. Registraton should succeed, as type registration/checking is deferred. + DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlConnection).AssemblyQualifiedName); + providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + // obtaining the factory will kick in the checks of the registered type name, which will cause exceptions. The checks were deferred till the GetFactory() call. + Assert.Throws(() => DbProviderFactories.GetFactory(providerTable.Rows[0])); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + } [Fact] public void UnregisterFactoryTest() @@ -158,7 +183,7 @@ private void ClearRegisteredFactories() // as the DbProviderFactories table is shared, for tests we need a clean one before a test starts to make sure the tests always succeed. Type type = typeof(DbProviderFactories); FieldInfo info = type.GetField("_registeredFactories", BindingFlags.NonPublic | BindingFlags.Static); - ConcurrentDictionary providerStorage = info.GetValue(null) as ConcurrentDictionary; + IDictionary providerStorage = info.GetValue(null) as IDictionary; Assert.NotNull(providerStorage); providerStorage.Clear(); Assert.Equal(0, providerStorage.Count); From 00cfd77cae54b108e3421642c87a5b39cf540cf3 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Tue, 21 Nov 2017 15:59:24 +0100 Subject: [PATCH 20/27] Final implementation --- .../System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs index 19cf1b4c77ad..204523823861 100644 --- a/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs +++ b/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -17,7 +17,6 @@ public sealed class TestProviderFactory : DbProviderFactory private TestProviderFactory() { } } - [Trait("Tests", "DbProviderFactories")] public class DbProviderFactoriesTests { [Fact] From 88722823d31492e3596bae1d7a845d44953c6253 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 1 Dec 2017 10:35:22 +0100 Subject: [PATCH 21/27] Corrections made to code by request of @saurabh500 --- .../src/System/Data/Common/DbProviderFactories.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index c2b85b59773e..3d011baa0edd 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -47,7 +47,7 @@ public static bool TryGetFactory(string providerInvariantName, out DbProviderFac public static DbProviderFactory GetFactory(string providerInvariantName) { - return GetFactory(providerInvariantName, throwOnError:true); + return GetFactory(providerInvariantName, throwOnError: true); } public static DbProviderFactory GetFactory(DataRow providerRow) @@ -86,7 +86,7 @@ public static DataTable GetFactoryClasses() DataColumn nameColumn = new DataColumn(Name, typeof(string)) { ReadOnly = true }; DataColumn descriptionColumn = new DataColumn(Description, typeof(string)) { ReadOnly = true }; DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)) { ReadOnly = true }; - DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)) { ReadOnly = true}; + DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)) { ReadOnly = true }; DataTable toReturn = new DataTable(ProviderGroup) { Locale = CultureInfo.InvariantCulture }; toReturn.Columns.AddRange(new[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }); From 89f73b2e78e6a25ffef8ab5577f4d9e8ab9a1e0c Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 1 Dec 2017 10:37:36 +0100 Subject: [PATCH 22/27] Github for windows didn't commit this file in last commit... :/ --- src/System.Data.Common/src/System.Data.Common.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/System.Data.Common/src/System.Data.Common.csproj b/src/System.Data.Common/src/System.Data.Common.csproj index 5e3d53dee503..a4c6b8452e06 100644 --- a/src/System.Data.Common/src/System.Data.Common.csproj +++ b/src/System.Data.Common/src/System.Data.Common.csproj @@ -57,9 +57,7 @@ - - Component - + From d19cf6aa2d7f7f92b5ff5766763f7cd8cc0a171b Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 1 Dec 2017 10:53:59 +0100 Subject: [PATCH 23/27] Again correcting component elements. These are automatically inserted so if they're back again, I can't remove them --- .../src/System.Data.Common.csproj | 42 +++++-------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/System.Data.Common/src/System.Data.Common.csproj b/src/System.Data.Common/src/System.Data.Common.csproj index a4c6b8452e06..826e4c1b5396 100644 --- a/src/System.Data.Common/src/System.Data.Common.csproj +++ b/src/System.Data.Common/src/System.Data.Common.csproj @@ -57,7 +57,7 @@ - + @@ -78,14 +78,10 @@ - - Component - + - - Component - + @@ -95,13 +91,9 @@ - - Component - + - - Component - + @@ -143,9 +135,7 @@ - - Component - + @@ -170,9 +160,7 @@ - - Component - + @@ -182,15 +170,9 @@ - - Component - - - Component - - - Component - + + + System\Data\Common\DbConnectionOptions.Common.cs @@ -201,9 +183,7 @@ - - Component - + From df9d9294ccfa58564bcf819522f5f70a0a3d1d9d Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 1 Dec 2017 10:56:35 +0100 Subject: [PATCH 24/27] ... annnnd another component element. Last one I hope --- src/System.Data.Common/tests/System.Data.Common.Tests.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index 14b510272424..7ba4b169b14c 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -77,9 +77,7 @@ - - Component - + From f79c02928f06efaba2500bbda370c296bd1c8d67 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Fri, 1 Dec 2017 13:15:35 +0100 Subject: [PATCH 25/27] @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 --- src/System.Data.Common/System.Data.Common.sln | 4 +- .../src/Resources/Strings.resx | 8 ++-- .../src/System.Data.Common.csproj | 44 ++++++++++++++----- .../System/Data/Common/DbProviderFactories.cs | 36 +++++++-------- .../tests/System.Data.Common.Tests.csproj | 4 +- 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/System.Data.Common/System.Data.Common.sln b/src/System.Data.Common/System.Data.Common.sln index 816b5fd8bf29..849ecb5f5fba 100644 --- a/src/System.Data.Common/System.Data.Common.sln +++ b/src/System.Data.Common/System.Data.Common.sln @@ -26,8 +26,8 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU diff --git a/src/System.Data.Common/src/Resources/Strings.resx b/src/System.Data.Common/src/Resources/Strings.resx index a0cee9a5519e..f13b9de2d8b7 100644 --- a/src/System.Data.Common/src/Resources/Strings.resx +++ b/src/System.Data.Common/src/Resources/Strings.resx @@ -512,9 +512,9 @@ Cannot remove this column, because it is part of an expression: {0} = {1}. The rowOrder value={0} has been found twice for table named '{1}'. Cannot find ElementType name='{0}'. - The specified invariant name '{0}' wasn't found in the list of registered .Net Framework Data Providers. - The requested .Net Framework Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type. - The registered .Net Framework Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded. - The missing .Net Framework Data Provider's assembly qualified name is required. + The specified invariant name '{0}' wasn't found in the list of registered .NET Data Providers. + The requested .NET Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type. + The registered .NET Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded. + The missing .NET Data Provider's assembly qualified name is required. The type '{0}' doesn't inherit from DbProviderFactory. diff --git a/src/System.Data.Common/src/System.Data.Common.csproj b/src/System.Data.Common/src/System.Data.Common.csproj index 826e4c1b5396..5e3d53dee503 100644 --- a/src/System.Data.Common/src/System.Data.Common.csproj +++ b/src/System.Data.Common/src/System.Data.Common.csproj @@ -57,7 +57,9 @@ - + + Component + @@ -78,10 +80,14 @@ - + + Component + - + + Component + @@ -91,9 +97,13 @@ - + + Component + - + + Component + @@ -135,7 +145,9 @@ - + + Component + @@ -160,7 +172,9 @@ - + + Component + @@ -170,9 +184,15 @@ - - - + + Component + + + Component + + + Component + System\Data\Common\DbConnectionOptions.Common.cs @@ -183,7 +203,9 @@ - + + Component + diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 3d011baa0edd..c74ad9ecf233 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Reflection; using System.Threading; namespace System.Data.Common @@ -32,12 +33,12 @@ internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProvide } private static ConcurrentDictionary _registeredFactories = new ConcurrentDictionary(); - private const string AssemblyQualifiedName = "AssemblyQualifiedName"; - private const string InvariantName = "InvariantName"; - private const string Name = "Name"; - private const string Description = "Description"; - private const string ProviderGroup = "DbProviderFactories"; - private const string Instance = "Instance"; + 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 Instance = nameof(Instance); public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) { @@ -54,7 +55,7 @@ public static DbProviderFactory GetFactory(DataRow providerRow) { ADP.CheckArgumentNull(providerRow, nameof(providerRow)); - DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName]; + DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedNameColumnName]; if (null == assemblyQualifiedNameColumn) { throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); @@ -83,21 +84,21 @@ public static DbProviderFactory GetFactory(DbConnection connection) public static DataTable GetFactoryClasses() { - DataColumn nameColumn = new DataColumn(Name, typeof(string)) { ReadOnly = true }; - DataColumn descriptionColumn = new DataColumn(Description, typeof(string)) { ReadOnly = true }; - DataColumn invariantNameColumn = new DataColumn(InvariantName, typeof(string)) { ReadOnly = true }; - DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedName, typeof(string)) { ReadOnly = true }; + 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(ProviderGroup) { Locale = CultureInfo.InvariantCulture }; + 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[InvariantName] = kvp.Key; - newRow[AssemblyQualifiedName] = kvp.Value.FactoryTypeAssemblyQualifiedName; - newRow[Name] = string.Empty; - newRow[Description] = string.Empty; + newRow[InvariantNameColumnName] = kvp.Key; + newRow[AssemblyQualifiedNameColumnName] = kvp.Value.FactoryTypeAssemblyQualifiedName; + newRow[NameColumnName] = string.Empty; + newRow[DescriptionColumnName] = string.Empty; toReturn.AddRow(newRow); } return toReturn; @@ -178,8 +179,7 @@ private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); } - System.Reflection.FieldInfo providerInstance = providerFactoryClass.GetField(Instance, System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static); + FieldInfo providerInstance = providerFactoryClass.GetField(Instance, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); if (null == providerInstance) { throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); diff --git a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index 7ba4b169b14c..14b510272424 100644 --- a/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -77,7 +77,9 @@ - + + Component + From 6868673d638f4372effcc638f62030abb0bad8a6 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Sat, 2 Dec 2017 11:03:22 +0100 Subject: [PATCH 26/27] 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 --- .../System/Data/Common/DbProviderFactories.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index c74ad9ecf233..01c77c625aa3 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -19,7 +19,6 @@ private struct ProviderRegistration { internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProviderFactory factoryInstance) { - ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName)); this.FactoryTypeAssemblyQualifiedName = factoryTypeAssemblyQualifiedName; this.FactoryInstance = factoryInstance; } @@ -38,7 +37,7 @@ internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProvide private const string NameColumnName = "Name"; private const string DescriptionColumnName = "Description"; private const string ProviderGroupColumnName = "DbProviderFactories"; - private const string Instance = nameof(Instance); + private const string InstanceFieldName = "Instance"; public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) { @@ -67,14 +66,10 @@ public static DbProviderFactory GetFactory(DataRow providerRow) throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); } - Type providerType = Type.GetType(assemblyQualifiedName); - if (null == providerType) - { - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); - } - return GetFactoryInstance(providerType); + return DbProviderFactories.GetFactoryInstance(DbProviderFactories.GetProviderTypeFromTypeName(assemblyQualifiedName)); } + public static DbProviderFactory GetFactory(DbConnection connection) { ADP.CheckArgumentNull(connection, nameof(connection)); @@ -160,12 +155,7 @@ private static DbProviderFactory GetFactory(string providerInvariantName, bool t // 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. - Type providerType = Type.GetType(registration.FactoryTypeAssemblyQualifiedName); - if (null == providerType) - { - throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, registration.FactoryTypeAssemblyQualifiedName)); - } - toReturn = GetFactoryInstance(providerType); + toReturn = GetFactoryInstance(GetProviderTypeFromTypeName(registration.FactoryTypeAssemblyQualifiedName)); RegisterFactory(providerInvariantName, toReturn); } return toReturn; @@ -179,7 +169,7 @@ private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); } - FieldInfo providerInstance = providerFactoryClass.GetField(Instance, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); + FieldInfo providerInstance = providerFactoryClass.GetField(InstanceFieldName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); if (null == providerInstance) { throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); @@ -195,5 +185,16 @@ private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) } 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; + } } } From 8d9834fcf0d51c34dd0477fdf75376262e21072b Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Mon, 4 Dec 2017 10:29:54 +0100 Subject: [PATCH 27/27] Last nit removal for @divega --- .../src/System/Data/Common/DbProviderFactories.cs | 4 ++-- src/System.Data.Common/tests/Configurations.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs index 01c77c625aa3..bc6c482faaf4 100644 --- a/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs +++ b/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -66,7 +66,7 @@ public static DbProviderFactory GetFactory(DataRow providerRow) throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); } - return DbProviderFactories.GetFactoryInstance(DbProviderFactories.GetProviderTypeFromTypeName(assemblyQualifiedName)); + return GetFactoryInstance(GetProviderTypeFromTypeName(assemblyQualifiedName)); } @@ -128,7 +128,7 @@ public static void RegisterFactory(string providerInvariantName, DbProviderFacto public static bool UnregisterFactory(string providerInvariantName) { - return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out ProviderRegistration providerRegistration); + return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out _); } private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) diff --git a/src/System.Data.Common/tests/Configurations.props b/src/System.Data.Common/tests/Configurations.props index 6090260affdf..8b803e0772f2 100644 --- a/src/System.Data.Common/tests/Configurations.props +++ b/src/System.Data.Common/tests/Configurations.props @@ -2,7 +2,7 @@ - netcoreapp; + netcoreapp; netstandard;