SqlClient: System.AccessViolationException at SNIOpenSyncExWrapper

Description

When connecting to a SQL instance in Azure App Services if the connection fails we don’t get a graceful connection error but we recieve the following exception.

This code is .NET Framework 4.8 Deployed to Azure App Services

Application: w3wp.exe
Framework Version: v4.0.30319
Description: System.AccessViolationException
Exception message: The process was terminated due to an unhandled exception.
Stack trace:

Exception Info: System.AccessViolationException
   at <Module>.SNIOpenSyncExWrapper(SNI_CLIENT_CONSUMER_INFO*, SNI_ConnWrapper**)
   at SNINativeMethodWrapper.SNIOpenSyncEx(ConsumerInfo, System.String, IntPtr ByRef, Byte[], Byte[], Boolean, Boolean, Int32, Boolean, Int32, Int32, Boolean)
   at System.Data.SqlClient.SNIHandle..ctor(ConsumerInfo, System.String, Byte[], Boolean, Int32, Byte[] ByRef, Boolean, Boolean, Boolean, System.Data.SqlClient.TransparentNetworkResolutionState, Int32)
   at System.Data.SqlClient.TdsParserStateObject.CreatePhysicalSNIHandle(System.String, Boolean, Int64, Byte[] ByRef, Byte[], Boolean, Boolean, Boolean, System.Data.SqlClient.TransparentNetworkResolutionState, Int32)
   at System.Data.SqlClient.TdsParser.Connect(System.Data.SqlClient.ServerInfo, System.Data.SqlClient.SqlInternalConnectionTds, Boolean, Int64, Boolean, Boolean, Boolean, Boolean, Boolean, System.Data.SqlClient.SqlAuthenticationMethod, Boolean, System.Data.SqlClient.SqlAuthenticationProviderManager)
   at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(System.Data.SqlClient.ServerInfo, System.String, System.Security.SecureString, Boolean, System.Data.ProviderBase.TimeoutTimer, Boolean, Boolean, Boolean)
   at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(System.Data.SqlClient.ServerInfo, System.String, System.Security.SecureString, Boolean, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SqlCredential, System.Data.ProviderBase.TimeoutTimer)
   at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(System.Data.ProviderBase.TimeoutTimer, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SqlCredential, System.String, System.Security.SecureString, Boolean)
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(System.Data.ProviderBase.DbConnectionPoolIdentity, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SqlCredential, System.Object, System.String, System.Security.SecureString, Boolean, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SessionData, System.Data.ProviderBase.DbConnectionPool, System.String, Boolean, System.Data.SqlClient.SqlAuthenticationProviderManager)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(System.Data.Common.DbConnectionOptions, System.Data.Common.DbConnectionPoolKey, System.Object, System.Data.ProviderBase.DbConnectionPool, System.Data.Common.DbConnection, System.Data.Common.DbConnectionOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(System.Data.ProviderBase.DbConnectionPool, System.Data.Common.DbConnection, System.Data.Common.DbConnectionOptions, System.Data.Common.DbConnectionPoolKey, System.Data.Common.DbConnectionOptions)
   at System.Data.ProviderBase.DbConnectionPool.CreateObject(System.Data.Common.DbConnection, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal)
   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(System.Data.Common.DbConnection, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(System.Data.Common.DbConnection, UInt32, Boolean, Boolean, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal ByRef)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(System.Data.Common.DbConnection, System.Threading.Tasks.TaskCompletionSource`1<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal ByRef)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(System.Data.Common.DbConnection, System.Threading.Tasks.TaskCompletionSource`1<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal, System.Data.ProviderBase.DbConnectionInternal ByRef)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(System.Data.Common.DbConnection, System.Data.ProviderBase.DbConnectionFactory, System.Threading.Tasks.TaskCompletionSource`1<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(System.Threading.Tasks.TaskCompletionSource`1<System.Data.ProviderBase.DbConnectionInternal>)
   at System.Data.SqlClient.SqlConnection.TryOpen(System.Threading.Tasks.TaskCompletionSource`1<System.Data.ProviderBase.DbConnectionInternal>)
   at System.Data.SqlClient.SqlConnection.Open()
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.TimerQueueTimer.CallCallback()
   at System.Threading.TimerQueueTimer.Fire()
   at System.Threading.TimerQueue.FireNextTimers()

To reproduce

Include a complete code listing (or project/solution) that we can run to reproduce the issue.

Partial code listings, or multiple fragments of code, will slow down our response or cause us to push the issue back to you to provide code to reproduce the issue.

private static bool TestConnection(string connectionString)
{
    const string SqlQuery = "SELECT TOP 1 message_id FROM sys.messages";
	
    try
    {
        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();

            using (var sqlCommand = new SqlCommand(SqlQuery, connection) {CommandType = CommandType.Text})
            {
                return sqlCommand.ExecuteReader().HasRows;
            }
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex);
        throw;
    }
}

Expected behavior

I’d expect if we’re having issues connecting to a SQL instance that a sensible error is returned. As the System.AccessViolationException bypasses the try { } catch { } due to behaviour changes in .NET Framework 4 we don’t want to set legacyCorruptedStateExceptionsPolicy due to the impact it would have on other exceptions.

Correct handling of the connection error and appropriate SQL exception raised back to the caller.

Further technical details

Microsoft.Data.SqlClient version: (found on the nuget or Microsoft.Data.SqlClient.dll) .NET target: Framework 4.8 SQL Server version: SQL Server 2017 (Distributed Availability Group) Operating system: App Services Windows

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (9 by maintainers)

Most upvoted comments

@ErikEJ next week is my last week in my current position and with my current company. Switching a key aspect of the project I am working on is not something I think is a good idea since I won’t be around to support it. 🙂

Edit: Perhaps saying a can of worms was not the best language to use, sorry about that.

@joe-green-uk this error as @lcheunglci mentioned is usually based on a mismatching between SNI library and M.D.S versions, in other words you are using a Microsoft.Data.SqlClient.SNI library which is not built for the in-use version of Microsoft.Data.SqlClient. Can you delete all M.D.S from your code (probably uninstall, clean your code) and reinstall M.D.S and let it decide about SNI version?

Thank you @JRahnama and @lcheunglci for your replies. Can we update the dependency information and setting a max version of Microsoft.Data.SqlClient.SNI to prevent this issue from coming up? Currently the dependency requires >= 2.1.1 and specifies no maximum version.