NullReference exception in CompilerServices.Symbols.Container.InvokeMethod

Wojciech Rajchel 21 Reputation points
2022-12-22T16:32:40.247+00:00

In an Asp.Net Web forms application I get regularly following exception

trace: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod(Method TargetProcedure, Object[] Arguments, Boolean[] CopyBack, BindingFlags Flags)
bei Microsoft.VisualBasic.CompilerServices.NewLateBinding.CallMethod(Container BaseReference, String MethodName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack, BindingFlags InvocationFlags, Boolean ReportErrors, ResolutionFailure& Failure)
bei Microsoft.VisualBasic.CompilerServices.NewLateBinding.ObjectLateCall(Object Instance, Type Type, String MemberName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack, Boolean IgnoreReturn)
bei Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCall(Object Instance, Type Type, String MemberName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack, Boolean IgnoreReturn)
bei DestroyDB()

The exception is thrown in the code that releases a db connection. The code is run with .Net 4.5 in IIS 10

Try
If Me.m_Conn Is Nothing Then Return True
If Not Me.m_ConnType = ConnectionType.Remote AndAlso Me.m_Conn.State = 1 Then Me.m_Conn.Close()
Return True
Catch ex As Exception
Finally
If Me.m_Conn IsNot Nothing Then Me.m_Conn.Dispose()
If Me.m_Transaction IsNot Nothing Then Me.m_Transaction.Dispose()
MyBase.Finalize()
End Try

We cannot reproduce when that problem happens. The application is used by multiple clients. Nobody reports the problem. Do you have an idea what can cause the exception?

ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,250 questions
0 comments No comments
{count} votes

Accepted answer
  1. Michael Taylor 47,806 Reputation points
    2022-12-22T17:03:18.28+00:00

    Your try-catch looks questionable. You didn't specify where this code is being called from but you should really never call the finalizer directly in your code. You're dealing with a disposable object and that is tricky. I don't believe your existing code is correct nor does it follow the recommended pattern for cleanup.

    The finalizer is called at some point before the object is released from memory. That may occur after the object has already been disposed (via a using) so your finalizer has to handle the fact that it might already be cleaned up. Furthermore GC doesn't guarantee ordering so when the finalizer is called your connection you have may or may not have already been cleaned up. It doesn't matter that you might still have a reference to it because the GC has already determined your object isn't used anymore and therefore anything it touches is free to be cleaned up. So, in a finalizer, you cannot touch any reference type fields you might have (such as your connection) because they may not be valid anymore.

    The net result is that when you are dealing with a type that has disposable resources you should follow the standard Dispose Pattern. The gist is given below with an attempt to convvert to VB.

       Public Class MyClass   
          Implements IDisposable  
         
          Protected Override Sub Finalize ()  
             Dispose(False)  
          End Sub  
            
          Public Sub Dispose () Implements IDisposable.Dispose  
             Dispose(True)  
             GC.SuppressFinalize(Me)       
          End Sub  
         
          Protected Overridable Sub Dispose ( disposing As Boolean )  
             If _disposed Then  
                Exit Sub  
             End If  
         
             If disposing Then  
                Dim conn = m_conn  
                m_conn = Nothing  
         
                If conn Is Not Nothing Then  
                   conn.Dispose()  
                End If  
            End If  
         
            _disposed = True  
          End Sub  
         
          Private Dim m_Conn As DbConnection  
          Private Dim _disposed As Boolean  
       End Class  
    

    The gist of this code is that when Dispose is called either via a Using statement or by the GC then your method is called with an indicator that it is safe to touch fields of reference types (because they are still referenced). You check to see if you need to clean them up and then have them dispose themselves. Because the object is now cleaned up you don't need the finalizer called so it is suppressed.

    If the finalizer is called without Dispose being called then the helper method is called but with an indicator that this is the finalizer and therefore reference fields cannot be touched as they may have already been cleaned up.

    Note that a dispose should never crash so some people also wrap the dispose contents in a try-catch if necessary.

    Finally note that an alternative approach to closing a connection is to check for the closed state and then close it but I find Dispose easier.

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Wojciech Rajchel 21 Reputation points
    2022-12-22T17:45:41.07+00:00

    Ok, thank you for the valuable answer. This code has worked for more than 10 years without throwing the exception. The exception started to happen after DB migration (client db access library migration). I know calling finalizer in the code is not a correct pattern. But I didn't realized it in this case because it worked so many years without logging any exceptions.

    0 comments No comments

  2. Wojciech Rajchel 21 Reputation points
    2022-12-26T11:33:11.533+00:00

    I have updated my call to simply make dispose call. I additionally added downcasting of connection objects. The Compilerservices exception was thrown because m_Conn and m_Transaction were declared as Object.

    >  Dim conn As Npgsql.NpgsqlConnection = CType(Me.m_Conn, Npgsql.NpgsqlConnection)  
    >    Dim trans As Npgsql.NpgsqlTransaction = CType(Me.m_Transaction, Npgsql.NpgsqlTransaction)  
    >   
    >    If conn IsNot Nothing Then conn.Dispose()  
    >    If trans IsNot Nothing Then trans.Dispose()  
    

    Now I am getting the following exception

    trace: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei Npgsql.NpgsqlConnection.Close(Boolean wasBroken)
    bei Npgsql.NpgsqlConnection.Dispose(Boolean disposing)
    bei System.ComponentModel.Component.Dispose()
    bei DestroyDB()

    It happens occasionally. What can cause this exception?