Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Implementation of DbProviderFactories. #25410

Merged

Conversation

FransBouma
Copy link

This PR implements API reviewed here: #20903 "Additional API for DbProviderFactories in .NET Core".
Fixes #4571

Design decisions:

  • GetFactory(DataRow) follows the same behavior as on NetFX: it will return the factory which type is present in the DataRow present. This can lead to undefined behavior when a different factory is registered in the DbProviderFactories for a given invariant name than the one specified in the DataRow. As this is a catch-22 situation with no true answer, the decision for now was made to make it behave the same as on NetFX
  • RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) registers the type name but doesn't check the type as that would require the load of the assembly and this method is meant to be used to pre-register factories which might not be present on the system (yet) (e.g. by an ORM). The type registration is deferred till the factory is requested through a GetFactory method. This might cause a false sense of correctness: the type registered might not be valid. This is a situation following from the nature of the method.
  • TryGetFactory(string providerInvariantName, out DbProviderFactory factory) will still throw an exception when the factory type associated with the providerInvariantName was registered with RegisterFactory(string, string) and the type is invalid, as the type checking was deferred when the registration took place. In the API review thread it was undecided what to do, so I decided to stick with the exception in this case: the alternative, swallow the exception and returning false could suggest the registration earlier was successful, or that the factory isn't there/not registered, which isn't the case: a factory is registered, the type specified just couldn't be loaded.
  • Internal implementation is using a ConcurrentDictionary instead of a DataTable, which is used in NetFx. This is done to avoid locks as the IEnumerable<string> GetProviderInvariantNames() method would lead to upscaling a ReaderWriterLockSlim lock to write level in the case of an internal Datatable
  • The method DataTable GetFactoryClasses() builds the datatable every time. Initially I had a cached version to avoid re-allocating columns again, but maintaining that construct was rather tedious and this method isn't expected to be on a hot path at all (it's likely called once in an application's life) so I've chosen for a lock-free rebuild of the datatable every time. NetFx creates a copy of the internal table, for comparison.
  • The method IEnumerable<string> GetProviderInvariantNames() returns a List<T> as that was the most simplest of implementations. If an array is preferred I could change that. As the internal structure is a concurrentdictionary and to keep things threadsafe, yield-ing over the keys set of the concurrent dictionary didn't feel correct, but please correct me if this should be updated. :)

// cc @karelz @divega @terrajobst

FransBouma added 23 commits May 17, 2017 12:24
- 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
- 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)
@saurabh500
Copy link
Contributor

@FransBouma @karelz I will be looking into the changes tomorrow.

@FransBouma
Copy link
Author

Ping @karelz @divega @saurabh500 and others?

Copy link
Contributor

@saurabh500 saurabh500 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me. Left some minor comments to be addressed or answered.

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};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: true}; Space after true

@@ -57,7 +57,9 @@
<Compile Include="System\Data\ConstraintCollection.cs" />
<Compile Include="System\Data\ConstraintConverter.cs" />
<Compile Include="System\Data\ConstraintEnumerator.cs" />
<Compile Include="System\Data\DataColumn.cs" />
<Compile Include="System\Data\DataColumn.cs">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The <SubType>Component</SubType> was added by VS .
Can you remove these tags please ? I wish I knew a way to turn off their generation in VS.

// 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a check for null or empty for registration.FactoryTypeAssemblyQualifiedName ?
Or is it that Type.GetType would return a null for an null or empty parameter passed in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for that is done at RegisterFactory(string, string), line 114:

        public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName)
        {
            ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName));
            ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName));

It then creates a ProviderRegistration class which on its own again checks for the value being not null and have a length, so it's not possible to have a name that's null or empty.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal, but my first thought when I saw the ProviderRegistration constructor is that it could validate that either the instance of the AQN are not null. If the instance is passed, the only case in which we may need to obtain the AQN again is GetProviderClasses, which should be used infrequently.

FWIW, even if we did that, this code would still be safe without a null check.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the struct is private and its ctor is used only in codepaths which indeed check inputs, the null check in the ProviderRegistration could currently be removed. I added it there for clarity as it shouldn't be null. It's easier for maintainers who might alter the code later on: they then don't have to worry if a code path could lead to a null value there. btw the netfx code contains duplicate null checks too in a code path, which I guess are there for similar reasons :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get all that, and I like that you put the check there... maybe I am not getting my main point across: if the instance is set, we can allow the AQN to be null.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I didn't understand that from your remark. If the instance is null, then indeed the name can be null, as the if at line 158 isn't true. But what shall I do now: wrap the check in an if (complexer code) ? Or leave it as is? Or remove the null check?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about this but thinking about it again, it seems that doing the validation on the constructor provides little value. E.g. there is still a default constructor that allows to create an invalid ProviderRegistration, and the validation hast to be performed elsewhere anyway.

I still believe we don't need to eagerly store the assembly qualified name in the ProviderRegistration when the instance is provided (we can compute it on the fly on GetFactoryClasses), but I don't feel super strongly about that either.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default ctor is indeed a mitigating factor for the check in the other ctor. I'll remove that one, and leave it at that. storing the aqn isn't harmful in any way, in fact, explicitly storing a null might raise an eyebrow as the question might arise "why is it deliberately left empty" which then requires a comment "we can recompute it later", IMHO unneeded complexity.


public static DbProviderFactory GetFactory(string providerInvariantName)
{
return GetFactory(providerInvariantName, throwOnError:true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Space before true throwOnError:true

@FransBouma
Copy link
Author

FransBouma commented Dec 1, 2017

The DataColumn class indeed got that Component element, I misunderstood the remarks made earlier, I thought it was about the Compile element. I've removed the component element. Decorating the class with [System.ComponentModel.DesignerCategory("Code")] might help though, e.g. double clicking the file will then open it in the text editor, and not in the 'designer view' which is useless for DataColumn anyway. But not sure of the impact on that wrt DataTable designers, so that's for another time.

Will correct the other requests too.

@FransBouma
Copy link
Author

Ugh, I see a lot of other files have component elements now too in the csproj. Will remove them. Sorry about that.

@FransBouma
Copy link
Author

FransBouma commented Dec 1, 2017

I can't get rid of the component elements... I remove them, I reload the project, I rebuild, I run the tests, and viola there they are again. This is in 2017 btw. Will now try vs2015, perhaps that's less problematic.

(edit) Issuing a 'build' or rebuild on the solution in vs recreates them. they're recreated every time I build the solution now in vs. I'll remove them and then commit the file. Why is this task so jinxed...

(edit) fixing the elements and then loading it into 2017 and building with ctrl-shift-B doesn't reapply the elements. Anyway, I'll commit it now and hopefully they're not reintroduced by some ghost. :/

… so if they're back again, I can't remove them
@@ -74,7 +77,9 @@
<Compile Include="System\Data\InRowChangingEventExceptionTest.cs" />
<Compile Include="System\Data\InvalidConstraintExceptionTest.cs" />
<Compile Include="System\Data\MissingPrimaryKeyExceptionTest.cs" />
<Compile Include="System\Data\MonkeyDataSet.cs" />
<Compile Include="System\Data\MonkeyDataSet.cs">
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot one... 😢

Copy link
Contributor

@saurabh500 saurabh500 Dec 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FransBouma Don't spend too much time removing <SubType>Component</SubType>
What we typically do on the team is, we make changes in VS and allow it to pollute the CSProj and then we open the CSProj in a text editor and fix the XML to remove the component tag and then run the tests.

Alternatively, the csproj can be manually modified in a text editor to add the Compile tags and the changes are committed. After that work on the project in VS and after editing code, discard any unnecessary changes to Csproj after previous commit to make sure that it is clean.

The reason I mention this cleanup is because VS tries to open Component marked files in Designer and many a times it fails and the developer has to right click on the file and select View Code.

However this is definitely not the crux of the PR :) Let's push to keep the ball rolling and address the other discussion comments opened by @divega
I could clean up the Component tags if they become a problem.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saurabh500 :) The issue with opening the file in the designer view is indeed annoying :) It can be overcome with the attribute I mentioned earlier: decorating a class with [System.ComponentModel.DesignerCategory("Code")] and then opening it in vs will open it in the code editor. I think the only problem is if you decorate a class with that, it obviously will never open in the designer ;) which is a problem for typed datasets and the like ;)

But agreed let's finish the last points and then wrap it up :) I think we're almost done with the changes, only the point regarding the null check in the ProviderRegistration ctor is left I think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that seems like the only open discussion point right now.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn’t know about [System.ComponentModel.DesignerCategory("Code")]. Thanks! I will leave it to @saurabh500 here but I will definitely consider using that in other repos. Even if it is a bit more code, it seems a small price to pay for not having to fight VS ever again on this.

@bricelam FYI

@divega
Copy link

divega commented Dec 1, 2017

I can't get rid of the component elements...

Up to @saurabh500 who I think asked for them to be removed, but in other repos we have decided not to fight VS on this. It is annoying but doesn't seem worth it until there is a way to turn the behavior off.

Copy link

@divega divega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still haven't looked at it all, but here are some comments.

@@ -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
Copy link

@divega divega Nov 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand enough about how the build works to understand why this change was needed, whether it is correct, and if is going to be sufficient (e.g. why the release configurations are not changing)?

Perhaps @saurabh500 will know this, but perhaps we need help from an corefx expert?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be an automated change due to some other change? I haven't changed anything deliberately in the solution nor csproj files, only to add the DbProviderFactories.cs class of course and the tests.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be. Do things work if you revert it?

Another related concern: not sure if we need to do anything special with the fact that the rest of System.Data.Common is .NET Standard 2.0 but this new class isn't.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverting it doesn't have any effect, tests (the project is the tests project) works fine. Will revert to 'netstandard' in next commit

@@ -512,4 +512,9 @@
<data name="DataColumns_RemoveExpression" xml:space="preserve"><value>Cannot remove this column, because it is part of an expression: {0} = {1}.</value></data>
<data name="DataRow_RowInsertTwice" xml:space="preserve"><value>The rowOrder value={0} has been found twice for table named '{1}'.</value></data>
<data name="Xml_ElementTypeNotFound" xml:space="preserve"><value>Cannot find ElementType name='{0}'.</value></data>
<data name="ADP_DbProviderFactories_InvariantNameNotFound" xml:space="preserve"><value>The specified invariant name '{0}' wasn't found in the list of registered .Net Framework Data Providers.</value></data>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.NET Framework Data Providers -> .NET Data Providers (applies to all strings in this list apparently).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, left over from the error messages from NetFX.

Copy link

@divega divega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good with some minor comments.

@ajcvickers any chance that you can review?

private const string InvariantName = "InvariantName";
private const string Name = "Name";
private const string Description = "Description";
private const string ProviderGroup = "DbProviderFactories";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: all these except AssemblyQualifiedName can be moved into GetFactoryClasses, correct?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably put something in the name to indicate which ones are column names, otherwise they could be defined as nameof() themselves, although that might look a bit weird.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rename the fields, good idea. I kept them together as it otherwise is a bit scattered. Now everything is discoverable in fixed locations (as one const indeed needs to be outside the method. )

private const string Name = "Name";
private const string Description = "Description";
private const string ProviderGroup = "DbProviderFactories";
private const string Instance = "Instance";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: perhaps just use nameof for this one?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know that worked, but it does indeed Instance = nameof(Instance)


namespace System.Data.Common
{
public static partial class DbProviderFactories
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add xml docs? The .NET Framework ones should be a good starting point.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought those weren't required? (as they're added elsewhere?) That's why I didn't add them

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. @karelz, @saurabh500?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at random other classes in this solution, none have xml doc comments.

{
ADP.CheckArgumentNull(providerRow, nameof(providerRow));

DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedName];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC @saurabh500 does not like var, but I don't like not using var when it is possible and clear, so I will call it out anyway 😸 Not sure if there are general guidelines for new code in corefx. @karelz?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guidelines say afaik that 'var' is discouraged, but have to look it up.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guidelines here: https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md point 10 says only about when not to use var, but it doesn't say use var when appropriate. There are many other cases in the class where var is an option. I didn't use that as the original code didn't and altering just for the sake of changing is also a bit much ;)

// 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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal, but my first thought when I saw the ProviderRegistration constructor is that it could validate that either the instance of the AQN are not null. If the instance is passed, the only case in which we may need to obtain the AQN again is GetProviderClasses, which should be used infrequently.

FWIW, even if we did that, this code would still be safe without a null check.

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 |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: using?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added using, cleaned up the BindingFlags statements too.

@@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be. Do things work if you revert it?

Another related concern: not sure if we need to do anything special with the fact that the rest of System.Data.Common is .NET Standard 2.0 but this new class isn't.

- 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
@ajcvickers
Copy link

@divega I wasn't planning to since I figured you and @saurabh500 have it covered, but I can add it to my list if you think it would be worthwhile.

private const string NameColumnName = "Name";
private const string DescriptionColumnName = "Description";
private const string ProviderGroupColumnName = "DbProviderFactories";
private const string Instance = nameof(Instance);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I had in mind for this one was to just inline it in GetFactoryInstance (I believe it is the only place where it is used), but regardless, now I think the recursive nameof() trick is not worth it. I would just use a string (either here or directly in the call to GetFiled. If you keep the const, perhaps rename me to InstanceFieldName.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're all used once except AssemblyQualifiedNameColumName, which is used 3 times. Having that one as constant and the rest as literal string is a bit awkward too. But in any case, I don't really see any harm having them as constants up top, it's the same in the NetFX code. I've reverted the nameof() change.

// 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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about this but thinking about it again, it seems that doing the validation on the constructor provides little value. E.g. there is still a default constructor that allows to create an invalid ProviderRegistration, and the validation hast to be performed elsewhere anyway.

I still believe we don't need to eagerly store the assembly qualified name in the ProviderRegistration when the instance is provided (we can compute it on the fly on GetFactoryClasses), but I don't feel super strongly about that either.

{
throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, registration.FactoryTypeAssemblyQualifiedName));
}
toReturn = GetFactoryInstance(providerType);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It seems the 6 lines above are repeated in GetFactory(providerRow), can extract another private method?

Copy link
Author

@FransBouma FransBouma Dec 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a clone indeed. I'll go with GetProviderTypeFromTypeName(aqn) and it will cover these lines, and always return a value (type instance) or throw the exception mentioned above and move the call inlined with getfactoryinstance call

throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, registration.FactoryTypeAssemblyQualifiedName));
}
toReturn = GetFactoryInstance(providerType);
RegisterFactory(providerInvariantName, toReturn);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to re-register here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's calling that method to simply re-use the mechanics used to register a factory, like overwriting an existing registration etc, as the current registration is a deferred one in that location. At that point it's the same situation as when a user had instantiated the factory manually and wants to register the factory. I didn't want to scatter manipulation of a registration object in multiple places.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense now that you explained it to me. It may be just me, but perhaps a code comment would help.

- 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
Copy link

@divega divega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a few nits that you may want to address. Nice test coverage.

throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName);
}

return DbProviderFactories.GetFactoryInstance(DbProviderFactories.GetProviderTypeFromTypeName(assemblyQualifiedName));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would leave the class name out. That would make the line shorter and more readable IMO.


public static bool UnregisterFactory(string providerInvariantName)
{
return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out ProviderRegistration providerRegistration);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: break the line on && and/or use out parameter discard syntax (just out _) to make this line shorter/easier to read.

}
else
{
if (string.IsNullOrWhiteSpace(providerInvariantName))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: collapse into else if?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate 'else if' as it has an 'else' block without curly braces, which I find personally not a good choice, every block should have curly braces, even if it's just 1 statement.

return null;
}
}
bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out ProviderRegistration registration);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: inline/remove wasRegistered variable if possible

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't possible as the 'registration' variable is declared at that line, so inlining it makes it go into the scope of the if true block, while it's used after that as well :) This can be solved then by declaring a line with the registration variable and that is mitigating the whole inlining act ;)

@@ -2,6 +2,7 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
netcoreapp;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indent

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabs vs. spaces... about that... haha no just kidding ;) Fixed.

@saurabh500
Copy link
Contributor

@dotnet-bot Test UWP CoreCLR x64 Debug Build please

@saurabh500 saurabh500 merged commit 7caa955 into dotnet:master Dec 5, 2017
@FransBouma
Copy link
Author

Thanks for merging! :)

@MarkusFritschi
Copy link

Thanks to you and everybody envolved in this project. Great job!

@karelz
Copy link
Member

karelz commented Dec 5, 2017

Indeed, great job @FransBouma! Thank you!
Also thanks @divega @saurabh500 @ajcvickers for guidance and helping to make it happen.

@karelz karelz added this to the 2.1.0 milestone Dec 5, 2017
@FransBouma FransBouma deleted the DbConnection_ProviderFactory_19826 branch December 5, 2017 18:22
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* Implements dotnet/corefx#19826: DbConnection doesn't offer an internal property ProviderFactory

* Added test for dotnet/corefx#19826. Due to project misconfiguration it fails locally.

* Removed writeline

* Added comment to illustrate the purpose of the property which currently is dead code.

* Implementation of DbProviderFactories, ported from NetFx and adjusted to CoreFx

See dotnet/corefx#4571, dotnet/corefx#19825 and dotnet/corefx#19826

* Update for project files to get a netcoreapp test set

* Changes to project system to get the stuff compiled. Failed.

* Various changes:

- Updated config/csproj files for netcoreapp for tests.
- Properly moved DbProviderFactories file to right location
- Added DbProviderFactories type to ref project, split across 2 files
(one for netcoreapp specific methods)
- Changed SetFactory to ConfigureFactory
- Changed generic type argument to normal method parameter
- Removed default parameter values and added overloads

* Added tests for DbProviderFactories.

* Better subclass testing added and more tests added

* Moved all hard-coded strings to SR/Strings.resx. Added test for factory retrieval using a connection

* Removal of now redundant comment. See: dotnet/corefx#19885 (review)

* Comment correction for bad English

* Refactored API a bit, see: dotnet/standard#356 (comment)

* Added tests for reworked API. Added tests for wrong type passing

* Reverting change in sln file: See dotnet/corefx#19885 (review)

* Almost done implementation using DataTable internal storage. Refactoring now to concurrentdictionary

* Final work for dotnet/corefx#20903

* Corrections of the implementation->

- registrations of assembly type name are now deferred
- storage is now a struct, as the typename has to be registrated as well.
- corrected a wrong nameof() call.
- added tests for the change behavior in RegisterFactory(string, string)

* Final implementation

* Corrections made to code by request of @saurabh500

* Github for windows didn't commit this file in last commit... :/

* Again correcting component elements. These are automatically inserted so if they're back again, I can't remove them

* ... annnnd another component element. Last one I hope

* @divega requested changes

- Corrected sln file netcoreapp->netstandard
- Corrected wording in exception messages.
- Component elements are back, I'll leave them now, they get back regardless.
- Renamed column name constants to have the 'ColumnName' suffix for more clarity
- Made Instance constant be initialized with nameof(Instance)
- Added using for System.Reflection so less verbose code

* More @divega requested changes

- factored out a clone into its own method (GetProviderTypeFromTypeName)
- Reverted 'nameof(Instance)' to string, and renamed field
- Removed redundant null/length check in ctor of ProviderRegistration ctor

* Last nit removal for @divega


Commit migrated from dotnet/corefx@7caa955
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants