Azure / aspnet-redis-providers

ASP.NET Redis Providers
Other
269 stars 181 forks source link

NullReferenceException #190

Closed timomta closed 1 year ago

timomta commented 1 year ago

I'm getting a null reference exception from the Redis ASP.Net session state provider via the StackExchangeClient. It is not coming directly from the application code, so I'm not sure exactly what is going on. Is it possible that the value of a given session state key/value pair was set to null and that caused an exception in the provider? For example, if Session("myKey") = null (a particular value was set to null) somehow happened, would that break the session state provider?

The application code (old code) I am working with works fine with the InProc provider.

</Data>
            <Data>removed</Data>
            <Data>/default.aspx</Data>
            <Data>10.18.1.133</Data>
            <Data></Data>
            <Data>False</Data>
            <Data></Data>
            <Data>IIS APPPOOL\removed</Data>
            <Data>63</Data>
            <Data>IIS APPPOOL\removed</Data>
            <Data>False</Data>
            <Data>   at System.Object.GetType()
   at Microsoft.Web.Redis.StackExchangeClientConnection.Eval(String script, String[] keyArgs, Object[] valueArgs)
   at Microsoft.Web.Redis.RedisConnectionWrapper.TryUpdateAndReleaseLock(Object lockId, ISessionStateItemCollection data, Int32 sessionTimeout)
   at Microsoft.Web.Redis.RedisSessionStateProvider.&lt;SetAndReleaseItemExclusiveAsync&gt;d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNet.SessionState.SessionStateModuleAsync.&lt;ReleaseStateAsyncImpl&gt;d__80.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNet.SessionState.TaskAsyncHelper.EndTask(IAsyncResult ar)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously)
</Data>

The exception is a null reference:

            <Data>NullReferenceException</Data>
            <Data>Object reference not set to an instance of an object.
   at System.Object.GetType()
   at Microsoft.Web.Redis.StackExchangeClientConnection.Eval(String script, String[] keyArgs, Object[] valueArgs)
   at Microsoft.Web.Redis.RedisConnectionWrapper.TryUpdateAndReleaseLock(Object lockId, ISessionStateItemCollection data, Int32 sessionTimeout)
   at Microsoft.Web.Redis.RedisSessionStateProvider.&lt;SetAndReleaseItemExclusiveAsync&gt;d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNet.SessionState.SessionStateModuleAsync.&lt;ReleaseStateAsyncImpl&gt;d__80.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNet.SessionState.TaskAsyncHelper.EndTask(IAsyncResult ar)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously)

Since it is thrown from session state client worker threads, it is difficult to determine the exact place in the application causing the issue.

timomta commented 1 year ago

I cloned the repo and debugged. The problem was that a class placed in session state was not marked serializable. As a result, serialization failed in this session provider. However, this provider swallows the exception (which actually states the problem and what it could not serialize) and returns a null:

RedisConnectionWrapper.cs

        private byte[] SerializeSessionStateItemCollection(ISessionStateItemCollection sessionStateItemCollection)
        {
            try
            {
                MemoryStream ms = new MemoryStream();
                BinaryWriter writer = new BinaryWriter(ms);
                ((SessionStateItemCollection)sessionStateItemCollection).Serialize(writer);
                writer.Close();
                return ms.ToArray();
            }
            catch <-- Exception swallowed
            {
                return null;
            }
        }

It would save a lot of debugging time if the exception was logged or communicated in some way. It isn't always clear that a particular class is not serializable, especially when embedded within a parent class.

stanleysmall-microsoft commented 1 year ago

Merged a PR removing trycatch from both serialization and deserialization methods

DarthSonic commented 1 year ago

Would be nice to get the class name where the missing serializable attribute was found within the exception.