Roslyn scripting engine does not throw runtime exception when used as delegate - c#

I run into troubles using the Roslyn Scripting Engine. I get no exception handling when I run a script within a delegation.
Test that works as expected:
string script = #"var a=0; var b=2/a;";
var runner = CSharpScript.Create<object>(script);
var errors = runner.Compile();
Assert.IsTrue(errors.IsEmpty);
try
{
runner.RunAsync();
Assert.Fail("Where is the exception");
}
catch (System.Exception)
{
// everything is OK! Error thrown...
}
Result: No Assertion. The Exception was thrown.
Here's the text using a delegate object:
Unittest:
string script = #"var a=0; var b=2/a;";
var runner = CSharpScript.Create<object>(script);
var errors = runner.Compile();
var delegat = runner.CreateDelegate();
Assert.IsTrue(errors.IsEmpty);
try
{
delegat();
Assert.Fail("Where is the exception?");
}
catch (System.DivideByZeroException)
{
// everything is OK! Error thrown...
}
I got the fail message and no exception was thrown.
We cache the delegates to speed up the compilation and during a test we see that runtime exceptions are not thrown. So I wrote the test to reproduce this situation.
I can't find any hint in the docs which describes that there are no exceptions thrown during the invoke.
Can someone give me a pointer or a hint why this happens?

There are two issues with your code:
In the first version, you're catching Exception, which means that when the Assert.Fail is reached and throws AssertionException, that exception is then caught and ignored.
This means that there is no difference between RunAsync and delegate here, neither of them throws DivideByZeroException.
Both RunAsync and the ScriptRunner<T> delegate return Task. That means to actually wait for them to complete or to observe any exceptions, you need to use await. Once you do that, you will see the DivideByZeroException that you're expecting.

Your Main finishes execution before the scheduler gets a chance to invoke delegat. It is a task that will run asynchronously. You can see that when you inspect it in the debugger:
To force execution inside the scope of the try...catch, you can use this:
try
{
delegat().Wait();
}
catch(System.AggregateException ex)
{
/* the inner exception of ex will be your DivideByZeroException */
}
The correct type of exception to expect in this case is the AggregateException, see here why.
A solution with await is also possible:
await delegat();
but this will compile only when the containing method can be marked async which is not necessarily what you want (show more of the calling code to clarify).

Related

Finally block does not execute as Microsoft say in it's Doc

Here Microsoft says:
"By using a finally block, you can clean up any resources that are
allocated in a try block, and you can run code even if an exception
occurs in the try block."
and clearly use this sample to support that idea:
public class ThrowTestA
{
static void Main()
{
int i = 123;
string s = "Some string";
object obj = s;
try
{
// Invalid conversion; obj contains a string, not a numeric type.
i = (int)obj;
// The following statement is not run.
Console.WriteLine("WriteLine at the end of the try block.");
}
finally
{
// To run the program in Visual Studio, type CTRL+F5. Then
// click Cancel in the error dialog.
Console.WriteLine("\nExecution of the finally block after an unhandled\n" +
"error depends on how the exception unwind operation is triggered.");
Console.WriteLine("i = {0}", i);
}
}
// Output:
// Unhandled Exception: System.InvalidCastException: Specified cast is not valid.
//
// Execution of the finally block after an unhandled
// error depends on how the exception unwind operation is triggered.
// i = 123
}
but when I copied this to my .Net-Core console it doesn't execute finally and doesn't return the output in finally that I expect according to Microsoft says! it returns below and I don't know why!
I know that we can simply use try..catch block and resolve the problem. I just want to know this contradiction with Microsoft's sample and my experience!
And later on in the same paper they say
Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up.
in your case the exception is unhandled so finally is not guaranteed to be called
Check it here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally
Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up.
Usually, when an unhandled exception ends an application, whether or not the finally block is run is not important. However, if you have statements in a finally block that must be run even in that situation, one solution is to add a catch block to the try-finally statement. Alternatively, you can catch the exception that might be thrown in the try block of a try-finally statement higher up the call stack. That is, you can catch the exception in the method that calls the method that contains the try-finally statement, or in the method that calls that method, or in any method in the call stack. If the exception is not caught, execution of the finally block depends on whether the operating system chooses to trigger an exception unwind operation.
When is this usually?
If the exception is not caught, execution of the finally block depends on whether the operating system chooses to trigger an exception unwind operation.
Exception unwind
When we enclose a block of code with a try statement compiler creates a step to store current context of the CPU in current stack frame with program counter pointed to start of catch block. If the CPU encounters any error it retrieves the context from stack an program counter jumps to catch statement.
Conditions when finally does not execute in a .net try..finally block
Some examples:
StackOverflowException
ExecutionEngineException
Application exit

Exception in async/await not caught

I do not understand why an exception thrown inside an async method is not caught when the await statement is surrounded by a try/catch, which becomes an unhandled exception crashing the app.
The reason for this exception is understood, I'm not expecting an answer for that. I was assuming the exception would be caught. I'm worried about the exception handling in the application not doing what is expected on future exceptions.
using Amazon.Lambda.Model;
InvokeResponse invokeResponse;
var lambdaRequest = new InvokeRequest {...};
try
{
var task = _awsLambdaClient.InvokeAsync(lambdaRequest);
invokeResponse = await task; <<== throws TaskCanceledException
}
catch (Exception e)
{
Log.Error(...);
return
}
C# environment is Mono, Xamarin.Android to be specific, though I imagine the same code compiled in a .NETCORE console app would repro as well, unless this is a bug in Mono which I doubt. I assume I'm misunderstanding something about exception handling.
UPDATE:
Tried the same code in a .NETCORE20 console application, and the TaskCanceledException is handled. So it's looking like a Mono/Xamarin.Android specific problem.
New Update: After being able to repro this for a few days, of course my repro stopped repro'ing and the exception is now caught in the Mono app too. Not really sure what happened.

Why do my C# unit tests for exceptions fail if I use try/catch?

I'm using C# with nUnit to unit test my project and I'm trying to write some unit tests that ensure a certain Exception is called but my tests keep failing. After a lot of testing to work out the issue, I managed to identify the cause of it and created a small code example that shows my issuew below.
My unit test code:
[Test]
public void TestExceptionIsRaised()
{
var ex = Assert.Throws<UnauthorizedAccessException>(() => TestExceptionMethod());
StringAssert.Contains("Attempted to perform an unauthorized operation", ex.Message);
}
My method with no error checking:
public void TestExceptionMethod()
{
throw new UnauthorizedAccessException();
}
Now if I run that unit test with the above method... it throws the exception, nUnit detects it and the test passes. However, this code is a problem because it has no error handling and will crash if I release it like this.
So to solve that problem, I add some error handling into my method like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
}
}
But now when I run the unit test it fails with this error:
Expected: System.UnauthorizedAccessException
But was: null
So rather than having to choose between a unit test or proper error handling, I tried to re-throw the error like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
throw;
}
}
Finally, everything now works. The unit test passes every time. And my method can still catch the error and do something about it.
However from what I've heard it isn't good practice to keep re-throwing errors like this??? So I'm just wondering is my solution "correct"? Or is there a better way of unit testing for an exception without having to remove the try/catch blocks from the original method?
However from what I've heard it isn't good practice to keep
re-throwing errors like this???
There is nothing wrong with re-throwing exceptions. It's actually a best practice to re-throw the exception after your application runs some logging code without recovering your application state.
Or is there a better way of unit testing for an exception without
having to remove the try/catch blocks from the original method?
I'm not sure I understand what you mean. But if you expect an exception thrown from your method, there must be an exception for your test to pass no matter it's thrown directly or re-thrown. If you try/catch like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
}
}
The test will fail for sure because the exception is swallowed. The test cannot detect there was an exception inside the method.
Translating your test into English you are saying:
"I expect the method TestExceptionMethod to throw a
UnauthorizedAccessException. The message of which will contain a
string with more details."
By not throwing the exception this test will never pass. If you don't want the exception to be thrown you need to change your test. If you do want the exception to be thrown, what you're doing is correct.
Is this correct?
It depends. If what you're doing is an exceptional issue, then throw an exception. Let the program crash. Fix the bug.
On the other hand if this is something that is to be expected such as bad user input, boundary conditions, handle it if you can. If you cannot recover from the problem then throw an exception.
Exception handling done poorly can make debugging more difficult especially when the exception is swallowed, or when a new exception instance is created and thrown losing the stack trace of the original exception. Sometimes the latter is desirable when you need a more abstract exception higher up the call stack, in which case you can add the original exception as an inner exception to keep access to the stack trace.

Throwing exception from Task.Run displays "Not Handled from User Code" Message

Code sample:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Console.WriteLine("Start");
Foo f = new Foo();
f.FooAsync();
}
public class Foo
{
public async void FooAsync()
{
try
{
await Task.Run(() =>
{
Console.WriteLine("Throwing");
throw new Exception();
});
}
catch (Exception)
{
Console.WriteLine("Caught");
}
}
}
}
The above code when run from C# fiddle or from the console on my PC prints:
Start
Throwing
Caught
C# Fiddle Example
However, when I run it from Visual Studio (F5 in debug mode), I get the following message:
I don't understand why I get "was not handled in user code" message from Visual Studio although running it from the console or C# fiddle is fine. Am I missing something obvious?
UPDATE
I've tried waiting on the task f.FooAsync().Wait(); in Main, but the exception is still reported as being unhandled (same error message).
First, what's special about this exception is that it's being thrown from Task.Run and for that scope, the exception really isn't handled by user-code.
The framework catches and stores the exception in the returned task for you to handle later, if you fail to do that it's an unobserved task exception (which in .Net 4.0 would've crashed the application).
The issue here is the "Enable Just My Code" feature in the settings. When the feature is turned on the debugger breaks on exceptions when you leave user-code and enter framework-code and at that point the exception really wasn't handled.
If you turn that feature off the debugger will not break on that exception as it will follow your code into the framework and see the exception being handled and stored in the task. The exception will then be re-thrown when you await the task and be handled by your code.
Since the handling that actually matters is the one being done by the framework, this is an even simpler example:
public static void Main()
{
Task.Run(() =>
{
throw new Exception();
});
Console.ReadLine();
}
With "Enable Just My Code" turned on the debugger will break on the exception, without it the debugger will not.

Exceptions not propagating from a reflected method call in c#

When calling a method via methodInfo.Invoke, if an exception is thrown it does not seem to propagate up to my catch blocks.
object value;
try
{
value = myMethod.Invoke(null, parameters);//program crashes with uncaught exception
}
catch
{
throw new Exception("Caught!");//never executed
}
The particular exception this method is raising is KeyNotFoundException, but that shouldn't matter because I'm catching everything right?
The particular error message I get from Visual Studio is
KeyNotFoundException was unhandled by user code
whereas normally the message would say
KeyNotFoundException was unhandled
if the call was not a reflected invocation.
I could just have the method check to see if they key is in there, and if not return null, but Using exception handling seems preferable. Is there any way to propagate exceptions up from a reflected method call?
This could be an issue with the Visual Studio debugger as well. As noted in the accepted answer to this similar question here, there are a few workarounds that you can do. The simplest of which is changing your Visual Studio debugger to turn off "Just My Code" in Tools -> Options -> Debugging -> General. You can also wrap it in a delegate or explicitly try to catch the invocation exception and inspect the inner exception of that, which should be your KeyNotFoundException.
It works for me:
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var method = typeof(Program).GetMethod("ThrowException");
try
{
method.Invoke(null, null);
}
catch (TargetInvocationException e)
{
Console.WriteLine("Caught exception: {0}", e.InnerException);
}
}
public static void ThrowException()
{
throw new Exception("Bang!");
}
}
Note that you do need to catch TargetInvocationException which is the exception thrown directly by Method.Invoke, wrapping the exception thrown by the method itself.
Can you come up with a similar short but complete program which demonstrates the problem?
If an error occurs during a method invoked with reflection, it should throw a TargetInvocationException that wraps (via .InnerException) the original. There are, however, a few methods that could cause more terminal fails, such as a few methods around winform creation / the message loop.
It is also possible that the method is working, but is causing additional work to happen on another thread, and it is that that is failing. This would kill the thread, and you can't catch it as it isn't on your thread. This would be particularly likely if your code is actually on a worker thread.

Categories

Resources