Exception.GetBaseException () returning an exception with an invalid InnerException value

I have this exception of type System.AggregateException :

 Message = "One or more errors occurred." Source = null StackTrace = null 

This InnerException is System.Reflection.TargetInvocationException :

 Message = "Exception has been thrown by the target of an invocation." Source = "mscorlib" StackTrace = at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.Incoming(IHubIncomingInvokerContext context) 

This InnerException is the exception I created that extends from ApplicationException :

 Message = "Some error writen by me at the hub" Source = "Chat" StackTrace = at Chat.Hubs.ChatHub.SendMessage(String text, String clientConnId) in d:\...Chat\Chat\Hubs\ChatHub.cs:line 48 

When I ran this:

 Exception excpetion = ex.GetBaseException(); 

Where ex is System.AggregateException , I get a System.Reflection.TargetInvocationException in excpetion . How can this happen?

Scenario

I do not know how to reproduce this in a simple project. In my case, I found this with a SignalR project. Some hub methods throw an exception, handle errors with this in global.asax:

 GlobalHost.HubPipeline.AddModule(new MyHubPipelineModule()); 

And MyHubPipelineModule should look like this:

 public class MyHubPipelineModule : HubPipelineModule { protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context) { Exception excpetion = ex.GetBaseException(); context.Hub.Clients.Caller.ExceptionHandler(excpetion.Message); } } 

Note. This should be done using SignalR 1.0.1. In version 1.1.0, the exception is simpler (smaller chain), so it works well. Make sure you have these package versions:

 <package id="Microsoft.AspNet.SignalR" version="1.0.1" targetFramework="net40" /> <package id="Microsoft.AspNet.SignalR.Core" version="1.0.1" targetFramework="net40" /> <package id="Microsoft.AspNet.SignalR.JS" version="1.0.1" targetFramework="net40" /> <package id="Microsoft.AspNet.SignalR.Owin" version="1.0.1" targetFramework="net40" /> <package id="Microsoft.AspNet.SignalR.SystemWeb" version="1.0.1" targetFramework="net40" /> 
+4
source share
3 answers

It seemed that the problem was not related to SignalR. This is due to AggregateExceptions .

I looked at the MSDN page of the AggregateException.GetBaseException method , and I found this:

Returns an AggregateException, which is the main reason for this exception.

Therefore, I assume that the documentation for the Exception.GetBaseException Method is only valid if the method is not overridden.

+1
source

According to MSDN, GetBaseException should behave like

 public Exception GetBaseException() { Exception result = this; while (result.InnerException != null) result = result.InnerException; return result; } 

And indicates

For all exceptions in the exception chain, the GetBaseException method should return the same object (the base exception).

What begs the question, why is this method virtual? It would seem that any redefinition may correspond to the implementation or violate the contract.

According to MSDN AggregateException.GetBaseException

Throws an AggregateException, which is the main reason for this exception.

In an OP statement (and source check), “Returns an AggregateException” may be violated because the result is not always an AggregateException exception.

Honestly, I believe that the whole expression is non-sequitur, since I believe that the "first" NON-AggregateException is the "main reason for this exception", because you can determine the root ("the") reason. Because "regular" exceptions simply wrap themselves in AggregateExceptions when one moves to the root of the parallel fan out processing.

My best interpretation is that AggregateException.GetBaseException is designed to "deploy" the meaningless, non-parallel nested values ​​of AggregateExceptions until it reaches the level at which the "shutdown" actually occurs.

The problem here will be what happens when there is no “fan”, that is, an AggregateException with one (non-aggregate) InnerException (s). In this case, it returns this (non-aggregate) exception ..... which has a different implementation / interpretation of GetBaseException.

+5
source

This is the source code for Mono:

 public override Exception GetBaseException() { Exception back = this; AggregateException backAsAggregate = this; while (backAsAggregate != null && backAsAggregate.InnerExceptions.Count == 1) { back = back.InnerException; backAsAggregate = back as AggregateException; } return back; } 

This means that GetBaseException () simply removes the topmost trivial wrappers of AggregateException.

In my opinion, the method should always be applied before reporting exceptions.

0
source

Source: https://habr.com/ru/post/1480948/


All Articles