JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.67k stars 3.24k forks source link

InvalidCastException deserializing a SqlException #2313

Closed amartini-n closed 4 years ago

amartini-n commented 4 years ago

Environment information

.NET Core: version 2.2 Newtonsoft.Json: version 11.0.1

Destination types

System.Data.SqlClient.SqlException

Source JSON

{
  "$type": "System.Data.SqlClient.SqlException, System.Data.SqlClient",
  "ClassName": "System.Data.SqlClient.SqlException",
  "Message": "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)",
  "Data": {
                "$type": "System.Collections.ListDictionaryInternal, System.Private.CoreLib",
    "HelpLink.ProdName": "Microsoft SQL Server",
    "HelpLink.EvtSrc": "MSSQLServer",
    "HelpLink.EvtID": "0",
    "HelpLink.BaseHelpUrl": "http://go.microsoft.com/fwlink",
    "HelpLink.LinkId": "20476",
    "SqlError 1": "System.Data.SqlClient.SqlError: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)"
  },
  "InnerException": null,
  "HelpURL": null,
  "StackTraceString": "   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)\\n   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)\\n   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)\\n   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)\\n   at System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()\\n--- End of stack trace from previous location where exception was thrown ---\\n   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)\\n   at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\Select.cs:line 106\\n   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncIterator.cs:line 98\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)\\n   at System.Collections.Generic.AsyncEnumerableHelpers.ToArrayWithLength[T](IAsyncEnumerable`1 source, CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncEnumerableHelpers.cs:line 48\\n   at System.Collections.Generic.AsyncEnumerableHelpers.ToArray[T](IAsyncEnumerable`1 source, CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncEnumerableHelpers.cs:line 16\\n   at Contoso.Manufacturing.Sublot.EntityFramework.Repositories.SublotRepository.LoadDataModelAsync(IEnumerable`1 ids) in /src/Sublot/Sublot.EntityFramework/Repositories/SublotRepository.cs:line 28\\n   at Contoso.Manufacturing.EntityFramework.Repository.DbMultiMapRepository`3.OnGetAsync(IEnumerable`1 ids)\\n   at Contoso.Manufacturing.Repository.MultiRepository`1.GetAsync(String id)\\n   at Contoso.Manufacturing.Sublot.Cqrs.Handlers.SublotHandler.Handle(MoveSublotCommand command) in /src/Sublot/Sublot.Cqrs.Handlers/SublotHandler.cs:line 193\\n   at Rebus.Pipeline.Receive.HandlerInvoker`1.Invoke()\\n   at Rebus.Pipeline.Receive.DispatchIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Sagas.LoadSagaDataStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Sagas.Exclusive.NewEnforceExclusiveSagaAccessIncomingStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.ActivateHandlersStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.HandleRoutingSlipsStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.Simple.FailedMessageWrapperStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.DeserializeIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.HandleDeferredMessagesStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.FailFast.FailFastStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.Simple.SimpleRetryStrategyStep.DispatchWithTrackerIdentifier(Func`1 next, String identifierToTrackMessageBy, ITransactionContext transactionContext, String messageId, String secondLevelMessageId)",
  "RemoteStackTraceString": null,
  "RemoteStackIndex": 0,
  "ExceptionMethod": null,
  "HResult": -2146232060,
  "Source": "Core .Net SqlClient Data Provider",
  "WatsonBuckets": null,
  "Errors": null,
  "ClientConnectionId": "00000000-0000-0000-0000-000000000000"
}

Expected behavior

I should be able to deserialize previous JSON string to an instance of SqlException by calling JsonConvert.DeserializeObject() and TypeNameHandling setting set to All.

Actual behavior

I got following exception:

  Message: 
    System.InvalidCastException : Unable to cast object of type 'Newtonsoft.Json.Linq.JValue' to type 'System.Guid'.
  Stack Trace: 
    SqlException.ctor(SerializationInfo si, StreamingContext sc)
    lambda_method(Closure , Object[] )
    JsonSerializerInternalReader.CreateISerializable(JsonReader reader, JsonISerializableContract contract, JsonProperty member, String id) line 1771
    JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) line 581
    JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) line 300
    JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) line 173
    JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) line 907
    JsonSerializer.Deserialize(JsonReader reader, Type objectType) line 886
    JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) line 837
    JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) line 792
    Issue9999_UNLISTED.Test() line 88

Steps to reproduce

            string json = @"
            {
              ""$type"": ""System.Data.SqlClient.SqlException, System.Data.SqlClient"",
              ""ClassName"": ""System.Data.SqlClient.SqlException"",
              ""Message"": ""A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)"",
              ""Data"": {
                            ""$type"": ""System.Collections.ListDictionaryInternal, System.Private.CoreLib"",
                ""HelpLink.ProdName"": ""Microsoft SQL Server"",
                ""HelpLink.EvtSrc"": ""MSSQLServer"",
                ""HelpLink.EvtID"": ""0"",
                ""HelpLink.BaseHelpUrl"": ""http://go.microsoft.com/fwlink"",
                ""HelpLink.LinkId"": ""20476"",
                ""SqlError 1"": ""System.Data.SqlClient.SqlError: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)""
              },
              ""InnerException"": null,
              ""HelpURL"": null,
              ""StackTraceString"": ""   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)\\n   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)\\n   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)\\n   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)\\n   at System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()\\n--- End of stack trace from previous location where exception was thrown ---\\n   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)\\n   at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\Select.cs:line 106\\n   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncIterator.cs:line 98\\n   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)\\n   at System.Collections.Generic.AsyncEnumerableHelpers.ToArrayWithLength[T](IAsyncEnumerable`1 source, CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncEnumerableHelpers.cs:line 48\\n   at System.Collections.Generic.AsyncEnumerableHelpers.ToArray[T](IAsyncEnumerable`1 source, CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\AsyncEnumerableHelpers.cs:line 16\\n   at Contoso.Manufacturing.Sublot.EntityFramework.Repositories.SublotRepository.LoadDataModelAsync(IEnumerable`1 ids) in /src/Sublot/Sublot.EntityFramework/Repositories/SublotRepository.cs:line 28\\n   at Contoso.Manufacturing.EntityFramework.Repository.DbMultiMapRepository`3.OnGetAsync(IEnumerable`1 ids)\\n   at Contoso.Manufacturing.Repository.MultiRepository`1.GetAsync(String id)\\n   at Contoso.Manufacturing.Sublot.Cqrs.Handlers.SublotHandler.Handle(MoveSublotCommand command) in /src/Sublot/Sublot.Cqrs.Handlers/SublotHandler.cs:line 193\\n   at Rebus.Pipeline.Receive.HandlerInvoker`1.Invoke()\\n   at Rebus.Pipeline.Receive.DispatchIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Sagas.LoadSagaDataStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Sagas.Exclusive.NewEnforceExclusiveSagaAccessIncomingStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.ActivateHandlersStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.HandleRoutingSlipsStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.Simple.FailedMessageWrapperStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.DeserializeIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Pipeline.Receive.HandleDeferredMessagesStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.FailFast.FailFastStep.Process(IncomingStepContext context, Func`1 next)\\n   at Rebus.Retry.Simple.SimpleRetryStrategyStep.DispatchWithTrackerIdentifier(Func`1 next, String identifierToTrackMessageBy, ITransactionContext transactionContext, String messageId, String secondLevelMessageId)"",
              ""RemoteStackTraceString"": null,
              ""RemoteStackIndex"": 0,
              ""ExceptionMethod"": null,
              ""HResult"": -2146232060,
              ""Source"": ""Core .Net SqlClient Data Provider"",
              ""WatsonBuckets"": null,
              ""Errors"": null,
              ""ClientConnectionId"": ""00000000-0000-0000-0000-000000000000""
            }
            ";

            var settings = new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All,
            }; 

            var sqlEx = JsonConvert.DeserializeObject<System.Data.SqlClient.SqlException>(json, settings);
amartini-n commented 4 years ago

The issue has been earlier posted on Stack Overflow: https://stackoverflow.com/questions/60726123/invalidcastexception-deserializing-a-sqlexception-with-newtonsoft-json. SqlException comes with ClientConnectionId property of type Guid. The error is raised in the constructor of the class with the (SerializationInfo si, StreamingContext sc) signature (see the implementation here below:

private SqlException(SerializationInfo si, StreamingContext sc) : base(si, sc)
        {
            HResult = SqlExceptionHResult;
            foreach (SerializationEntry siEntry in si)
            {
                if ("ClientConnectionId" == siEntry.Name)
                {
                    _clientConnectionId = (Guid)siEntry.Value;
                    break;
                }
            }
        }

I guess the issue is not in this constructor but in the reason why siEntry.Value is set to an instance of a type that could not be casted to a Guid (e.g. string).

amartini-n commented 4 years ago

Sorry, this issue is nonsense. There's no way for Newtonsoft JSON library to provide something different from JValue instances as values of SerializationEntry items (of SerializationInfo instance in ISerializable constructor). It is the constructor of target class implementing ISerializable that has the knowledge of itself and that should be able to handle SerializationEntry items differently e.g. by first of all testing if they are instances of the target type or they implementIConvertible . For the reason, I'm closing the issue.