Hey C# people, how do I throw an exception I just deserialized without modifying it?



  • Gist: https://gist.github.com/BenLubar/e67f3b3e56eddf0ff517b675ad0e81dc

    Expected output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at ExceptionStackKeeper.Program1.BarAsync[T](T obj) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program1.cs:line 41
       at ExceptionStackKeeper.Program1.<FooAsync>d__1.MoveNext() in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program1.cs:line 35
    

    Obviously wrong solution:

    throw deserializedException;
    

    Actual output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at ExceptionStackKeeper.Program2.Main(String[] args) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program2.cs:line 27
    

    Supposedly correct but also wrong solution:

    ExceptionDispatchInfo.Capture(deserializedException).Throw();
    

    Actual output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       at ExceptionStackKeeper.Program2.Main(String[] args) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program2.cs:line 19
    


  • @ben_lubar I'd throw a new Exception (of some type) and put your de-serialized Exception in the InnerException field.

    AFAIK there's no way to re-throw an Exception as if it were the first time.


  • Winner of the 2016 Presidential Election

    @ben_lubar said in Hey C# people, how do I throw an exception I just deserialized without modifying it?:

    Gist: https://gist.github.com/BenLubar/e67f3b3e56eddf0ff517b675ad0e81dc

    Expected output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at ExceptionStackKeeper.Program1.BarAsync[T](T obj) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program1.cs:line 41
       at ExceptionStackKeeper.Program1.<FooAsync>d__1.MoveNext() in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program1.cs:line 35
    

    Obviously wrong solution:

    throw deserializedException;
    

    Actual output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at ExceptionStackKeeper.Program2.Main(String[] args) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program2.cs:line 27
    

    Supposedly correct but also wrong solution:

    ExceptionDispatchInfo.Capture(deserializedException).Throw();
    

    Actual output:

    System.ArgumentNullException: Value cannot be null.
    Parameter name: obj
       at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       at ExceptionStackKeeper.Program2.Main(String[] args) in C:\Users\Owner\Source\Repos\ExceptionStackKeeper\ExceptionStackKeeper\Program2.cs:line 19
    

    This says the "Supposedly correct but also wrong solution" should look like this:

    catch(AggregateException exception)
    {
      exception = exception.Flatten();
      ExceptionDispatchInfo.Capture(
        exception.InnerException).Throw();
    }
    

    I do not know if there's any meaningful differences in there or not. Do you have a small demo of the problem to play with? I figure there's got a to be a way somehow, since the framework does it some places, right?



  • @dreikin said in Hey C# people, how do I throw an exception I just deserialized without modifying it?:

    Do you have a small demo of the problem to play with?

    The gist link at the top of the OP has source code for my test program. Basically, run Program1, copy the output to the specified location in Program2, and run Program2 to reproduce my output.


  • Winner of the 2016 Presidential Election

    @ben_lubar said in Hey C# people, how do I throw an exception I just deserialized without modifying it?:

    @dreikin said in Hey C# people, how do I throw an exception I just deserialized without modifying it?:

    Do you have a small demo of the problem to play with?

    The gist link at the top of the OP has source code for my test program. Basically, run Program1, copy the output to the specified location in Program2, and run Program2 to reproduce my output.

    D'oh! :facepalm: yes, forgot about that, thanks.

    However, looking further I found this StackOverflow comment:

    It seems that .net-4.5 added a new API for capturing stack/info about exceptions and rethrowing them in different contexts. This is called [`ExceptionDispatchInfo`](https://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo%28v=vs.110%29.aspx). It is useful if you find yourself needing more control over running tasks indirectly, like if you do manual thread management for jobs or `Task` does not exactly fit your needs. In your example, it should look like this:
    public ExceptionDispatchInfo _error { get; private set; }
    
    public bool IsValid()
    {
        try
        {
            //do something here to cause exception                
    
            return true;
        }
        catch (Exception ex)
        {
            _error = ExceptionDispatchInfo.Capture(ex);
            return false;
        }
    }
    
    /// <summary>Throw underlying exception if invalid.</summary>
    public void AssertWasValid() => _error?.Throw();
    

    Now, it doesn’t preserve the original caller. The displayed stack trace shows the calls from the original try block into the code in there, a statement breaking the original and new parts of the stack, and then the calls into ExceptionDispatchInfo.Throw() itself as the new part of the shown stack. This seems similar to how traces with async code look. If you care about the original caller, seems this won’t work. But if you care about getting the line/method that threw the exception, this should be sufficient.


  • Garbage Person

    AFAIK, you don't. Attach it to another exception as an innerexception


Log in to reply