Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Presence of various section in app.config breaks initialiser #558

Closed
jsobell opened this issue May 12, 2020 · 7 comments · Fixed by #573
Closed

Presence of various section in app.config breaks initialiser #558

jsobell opened this issue May 12, 2020 · 7 comments · Fixed by #573

Comments

@jsobell
Copy link

jsobell commented May 12, 2020

The presence of a configSections in an app.config breaks the connection initialiser.
To reproduce just create the most basic app possible (server name etc. is irrelevant):

using Microsoft.Data.SqlClient;

namespace TestSQL
{
    class Program
    {
        static void Main(string[] args)
        {
            var connection = new SqlConnection("data source=.;initial catalog=blah;Trusted_Connection=Yes;persist security info=False");
            connection.Open();
        }
    }
}

Adding an app.config with various combinations of sections such as

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="ConfigFolder" value="./config" />
    </appSettings>
    <configSections>
        <clear />
    </configSections>
</configuration>

causes the exception, as does adding a <system.diagnostics> section.

Unhandled exception. System.TypeInitializationException: The type initializer for 'Microsoft.Data.SqlClient.SqlAuthenticationProviderManager' threw an exception.
 ---> System.InvalidOperationException: Failed to read the config section for authentication providers.
 ---> System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize
 ---> System.Configuration.ConfigurationErrorsException: Only one <configSections> element allowed per config file and if present must be the first child of the root <configuration> element. (C:\Users\Jason\RiderProjects\TestSQL\TestSQL\bin\Debug\netcoreapp3.
0\TestSQL.dll.config line 6)
   at System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
   at System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors)
   at System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   --- End of inner exception stack trace ---
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   at System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at Microsoft.Data.SqlClient.SqlAuthenticationProviderManager..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.Data.SqlClient.SqlAuthenticationProviderManager..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserIn
stance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool, SqlAuthenticationProviderManager sqlAuthProviderManager)
   at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at Microsoft.Data.SqlClient.SqlConnection.Open()
   at TestSQL.Program.Main(String[] args) in C:\Users\Jason\RiderProjects\TestSQL\TestSQL\Program.cs:line 10

This is on .Net Core 3.0 or 3.1, Microsoft.Data.SqlClient, Version=1.12.20106.1

@ErikEJ
Copy link
Contributor

ErikEJ commented May 12, 2020

Could you share C:\Users\Jason\RiderProjects\TestSQL\TestSQL\bin\Debug\netcoreapp3.
0\TestSQL.dll.config

@jsobell
Copy link
Author

jsobell commented May 12, 2020

Sure, this one fails with the exception:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.diagnostics>
        <trace autoflush="true">
            <listeners>
                <add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener"/>
            </listeners>
        </trace>
    </system.diagnostics>
</configuration>

Adding only an appSettings section give a working TestSQL.dll.config of this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <clear/>
        <add key="flibble" value="blah" />
    </appSettings>
</configuration>

@DavoudEshtehari
Copy link
Contributor

Hi @jsobell,

According to Microsoft documentation

If <configSections> element is in a configuration file, it must be the first child element of the <configuration> element.

I tried the sample you provided with this fix and it worked fine. Please give it a try and let me know if it fixes the issue.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <clear />
  </configSections>
  <appSettings>
    <add key="ConfigFolder" value="./config" />
  </appSettings>
</configuration>

@jsobell
Copy link
Author

jsobell commented May 14, 2020

Well that might explain why that tag in that way causes an issue, but it does leave a couple of questions:

  1. According to the xsd schema, all of the above structures are valid, and all pass validation against DotNetConfig.xsd, which is not at all helpful :)
  2. The SQLClient is acting as a config validator, and (more importantly) is reporting an unrelated exception when it's unhappy with previously acceptable content
  3. If the config format is to enforce compliance with a non-existent schema, should this be documented as a breaking change (in .Net Core)?

If I add a definition for the subsection, the error is avoided, so hopefully this will help anyone else chasing this error:

  <configSections>
    <section name="system.diagnostics" type="System.Configuration.SingleTagSectionHandler" />
  </configSections>

but given that this has never been a requirement in the past, is it right to enforce it in SqlClient?
Using the ConfigurationManager to load this "invalid" config file (ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)) gives no error, but specifying that the appSettings are wanted (ConfigurationManager.AppSettings) gives a nested exception with System.Configuration.ConfigurationErrorsException: Unrecognized configuration section system.diagnostics, so the handling of "invalid" structures is inconsistent.
If (as in many cases) there is nothing in app.config that the developer expects SqlClient to read, should the provider be forcing the compliance of a config file that it isn't using?

At the very least, let's get an appropriate exception thrown by the client :)

@DavoudEshtehari
Copy link
Contributor

I added it to more investigation for adding an appropriate exception.
Also, can you share the new App.config because my code did not fix with the new change you mentioned in <configSections>?

@cheenamalhotra
Copy link
Member

@jsobell

Please give PR #573 a try and let us know!

@jsobell
Copy link
Author

jsobell commented May 25, 2020

Awesome speedy work @cheenamalhotra
We're still on 1.1 so can't test, but did a code review and looks perfect, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants