I am trying to use Fody to wrap all exceptions thrown from a method with a common exception format.
So I have added the required interface declaration and class implementation that looks like this :
using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
[module: MethodDecorator]
public interface IMethodDecorator
{
void Init(object instance, MethodBase method, object[] args);
void OnEntry();
void OnExit();
void OnException(Exception exception);
void OnTaskContinuation(Task t);
}
[AttributeUsage(
AttributeTargets.Module |
AttributeTargets.Method |
AttributeTargets.Assembly |
AttributeTargets.Constructor, AllowMultiple = true)]
public class MethodDecorator : Attribute, IMethodDecorator
{
public virtual void Init(object instance, MethodBase method, object[] args) { }
public void OnEntry()
{
Debug.WriteLine("base on entry");
}
public virtual void OnException(Exception exception)
{
Debug.WriteLine("base on exception");
}
public void OnExit()
{
Debug.WriteLine("base on exit");
}
public void OnTaskContinuation(Task t)
{
Debug.WriteLine("base on continue");
}
}
And the domain implementation that looks like this
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
namespace CC.Spikes.AOP.Fody
{
public class FodyError : MethodDecorator
{
public string TranslationKey { get; set; }
public Type ExceptionType { get; set; }
public override void Init(object instance, MethodBase method, object[] args)
{
SetProperties(method);
}
private void SetProperties(MethodBase method)
{
var attribute = method.CustomAttributes.First(n => n.AttributeType.Name == nameof(FodyError));
var translation = attribute
.NamedArguments
.First(n => n.MemberName == nameof(TranslationKey))
.TypedValue
.Value
as string;
var exceptionType = attribute
.NamedArguments
.First(n => n.MemberName == nameof(ExceptionType))
.TypedValue
.Value
as Type;
TranslationKey = translation;
ExceptionType = exceptionType;
}
public override void OnException(Exception exception)
{
Debug.WriteLine("entering fody error exception");
if (exception.GetType() != ExceptionType)
{
Debug.WriteLine("rethrowing fody error exception");
//rethrow without losing stacktrace
ExceptionDispatchInfo.Capture(exception).Throw();
}
Debug.WriteLine("creating new fody error exception");
throw new FodyDangerException(TranslationKey, exception);
}
}
public class FodyDangerException : Exception
{
public string CallState { get; set; }
public FodyDangerException(string message, Exception error) : base(message, error)
{
}
}
}
This works fine for synchronous code. But for asynchronous code the exception handler is skipped, even though all the other IMethodDecorator are executed (like OnExit, and OnTaskContinuation).
For example, looking at the following test class :
public class FodyTestStub
{
[FodyError(ExceptionType = typeof(NullReferenceException), TranslationKey = "EN_WHATEVER")]
public async Task ShouldGetErrorAsync()
{
await Task.Delay(200);
throw new NullReferenceException();
}
public async Task ShouldGetErrorAsync2()
{
await Task.Delay(200);
throw new NullReferenceException();
}
}
I see that ShouldGetErrorAsync produces the following IL code :
// CC.Spikes.AOP.Fody.FodyTestStub
[FodyError(ExceptionType = typeof(NullReferenceException), TranslationKey = "EN_WHATEVER"), DebuggerStepThrough, AsyncStateMachine(typeof(FodyTestStub.<ShouldGetErrorAsync>d__3))]
public Task ShouldGetErrorAsync()
{
MethodBase methodFromHandle = MethodBase.GetMethodFromHandle(methodof(FodyTestStub.ShouldGetErrorAsync()).MethodHandle, typeof(FodyTestStub).TypeHandle);
FodyError fodyError = (FodyError)Activator.CreateInstance(typeof(FodyError));
object[] args = new object[0];
fodyError.Init(this, methodFromHandle, args);
fodyError.OnEntry();
Task task;
try
{
FodyTestStub.<ShouldGetErrorAsync>d__3 <ShouldGetErrorAsync>d__ = new FodyTestStub.<ShouldGetErrorAsync>d__3();
<ShouldGetErrorAsync>d__.<>4__this = this;
<ShouldGetErrorAsync>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<ShouldGetErrorAsync>d__.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = <ShouldGetErrorAsync>d__.<>t__builder;
<>t__builder.Start<FodyTestStub.<ShouldGetErrorAsync>d__3>(ref <ShouldGetErrorAsync>d__);
task = <ShouldGetErrorAsync>d__.<>t__builder.Task;
fodyError.OnExit();
}
catch (Exception exception)
{
fodyError.OnException(exception);
throw;
}
return task;
}
And ShouldGetErrorAsync2 generates :
// CC.Spikes.AOP.Fody.FodyTestStub
[DebuggerStepThrough, AsyncStateMachine(typeof(FodyTestStub.<ShouldGetErrorAsync2>d__4))]
public Task ShouldGetErrorAsync2()
{
FodyTestStub.<ShouldGetErrorAsync2>d__4 <ShouldGetErrorAsync2>d__ = new FodyTestStub.<ShouldGetErrorAsync2>d__4();
<ShouldGetErrorAsync2>d__.<>4__this = this;
<ShouldGetErrorAsync2>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<ShouldGetErrorAsync2>d__.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = <ShouldGetErrorAsync2>d__.<>t__builder;
<>t__builder.Start<FodyTestStub.<ShouldGetErrorAsync2>d__4>(ref <ShouldGetErrorAsync2>d__);
return <ShouldGetErrorAsync2>d__.<>t__builder.Task;
}
If I call ShouldGetErrorAsync, Fody is intercepting the call, and wrapping the method body in a try catch. But if the method is async, it never hits the catch statement even though the fodyError.OnTaskContinuation(task) and fodyError.OnExit() are still called.
On the other hand, ShouldGetErrorAsync will handle the error just fine, even though there is no error handling block in the IL.
My question is, how should Fody be generating the IL to properly inject the error block and make it so async errors are intercepted?
Here is a repo with tests that reproduces the issue
You are only placing the try-catch around the content of the 'kick-off' method, this will only protect you up to the point where it first needs to reschedule (the 'kick-off' method will end when the async method first needs to reschedule and so will not be on the stack when the async method resumes).
You should look at modifying the method implementing IAsyncStateMachine.MoveNext() on the state machine instead. In particular, look for the call to SetException(Exception) on the async method builder (AsyncVoidMethodBuilder, AsyncTaskMethodBuilder or AsyncTaskMethodBuilder<TResult>) and wrap the exception just before passing it in.
await sure makes asynchronous methods look simple, doesn't it? :) You just found a leak in that abstraction - the method usually returns as soon as the first await is found, and your exception helper has no way to intercept any later exceptions.
What you need to do is implement both the OnException, and handle the return value from the method. When the method returns, and the task isn't completed, you need to wind up an error continuation on the task, which needs to handle exceptions the way you want them to be handled. The Fody guys thought of that - that's what the OnTaskContinuation is for. You need to check the Task.Exception to see if there's an exception lurking in the task, and handle it however you need to.
I think this will only work if you want to rethrow the exception while doing logging or something - it does not allow you to replace the exception with something different. You should test that :)
Related
I'm trying to make an exception interceptor in my Xamarin application. Right now I'm juste trying to intercept service's methods: the call from view model to buisiness logic (all in one project, full .net standard 2).
I fall upon this answer (using autofac) and found it simple and clever. It works fine, I add a try-catch to get my exception, so far so good.
But then I tried to return my exception in a DTO object type. All our services return a Task of a DTO class derived from a DTOBase abstract class. Theses classes just hold a reference to the value(s) and a IEnumerable of exception named Errors.
So basically, I try to catch the exception, put it in the list of Errors and return my object. I finished with this code :
public class ExceptionInterceptorBehaviour : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue);
}
}
private static async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
}
private static async Task<T> InterceptAsync<T>(Task<T> task)
{
try
{
T result = await task.ConfigureAwait(false);
return result;
}
catch (Exception e)
{
if (typeof(DTOBase).IsAssignableFrom(typeof(T)))
{
var ret = Activator.CreateInstance(typeof(T));
(ret as DTOBase).Errors.Add(e);
return (T)ret;
}
throw e;
}
}
}
My probleme is that the application crashes at the return of Task<T> InterceptAsync<T>(Task<T> task). No exception is raised, no pause mode in the debugger just a plain crash.
I suspect a segmentation error, but my cast does work (I tested it) and I do return a Task<T> and assign it to a Task<T> object.
Am I missing something? I don't get why it crashes like that.
Is that happening on iOS? Xamarin has some limitations defined by its underlying platforms. Dynamic code is one of them. Avoid using dynamic.
So, I took account of rubo's answer and rewrite my code with no dynamic variable and end up with this :
public class ExceptionInterceptorBehaviour : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
if (method.ReturnType.IsGenericType)
{
invocation.ReturnValue = typeof(ExceptionInterceptorBehaviour)
.GetMethod("InterceptGenericAsync", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(method.ReturnType.GenericTypeArguments[0])
.Invoke(this, new object[] { invocation.ReturnValue });
}
else
{
invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue);
}
}
}
private async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
}
private async Task<T> InterceptGenericAsync<T>(Task<T> task)
{
try
{
object result = await task.ConfigureAwait(false);
return (T)result;
}
catch (Exception e)
{
if (typeof(DTOBase).IsAssignableFrom(typeof(T)))
{
var ret = Activator.CreateInstance(typeof(T));
(ret as DTOBase).Errors.Add(e);
return (T)ret;
}
throw e;
}
}
}
The fun fact is that code was still crashing when I tried to step out of InterceptGenericAsync in debug, but it works just fine if I let it run, which is weird and scary.
I did not test this solution on iOS though, I'm not sure it's working.
I am writing a small wrapper (MyWrapper) for use in unit tests. Its purpose is to wrap test code with a try-catch in order to catch one specific exception (MySpecialException) and then ignore the test.
Why I do that should not be relevant for this question.
Given the code below, how do I prevent others from passing an Action and using async like this?
Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>) instead?
using System;
using System.Threading.Tasks;
using NUnit.Framework;
namespace PreventAsyncActionLambdaExample
{
[TestFixture]
public class Example
{
[Test]
public async Task ExampleTest()
{
// How do I prevent others from passing an action and using async like this?
// Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>) instead?
MyWrapper.Execute(async () =>
{
var cut = new ClassUnderTest();
await cut.DoSomethingAsync();
Assert.Fail("Problem: This line will never be reached");
});
}
}
public static class MyWrapper
{
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
action();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
// This method SHOULD BE USED in this example, BUT WILL NOT be used.
public static async Task ExecuteAsync(Func<Task> func)
{
try
{
await func();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
}
public class MySpecialException : Exception
{
// This is another exception in reality which is not relevant for this example
}
public class ClassUnderTest
{
public Task DoSomethingAsync()
{
return Task.Delay(20); // Simulate some work
}
}
}
I am afraid that you can't really prevent this at compile-time but you could write another overload that will be picked-up in this case to tell them that they are supposed to use ExecuteAsync instead:
public static Task Execute(Func<Task> action)
{
throw new Exception("Please use the ExecuteAsync(Func<Task> func) method instead if you will be passing async lambdas");
}
As mentioned in other answers, I do not think you can prevent it in compile time. However, you can do a hacky workaround and throw an exception. Inspired by this answer. It might not be a good solution, but it could at least make the test fail.
public static bool IsThisAsync(Action action)
{
return action.Method.IsDefined(typeof(AsyncStateMachineAttribute),
false);
}
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
if (IsThisAsync(action))
{
Console.WriteLine("Is async");
throw new ArgumentException("Action cannot be async.", nameof(action));
}
else
{
Console.WriteLine("Is not async");
}
action();
}
catch (MySpecialException)
{
}
}
And tests:
[TestClass]
public class MyWrapperTests
{
// Will not pass
[TestMethod]
public void ShouldAllowAsyncAction()
{
// This will throw an exception
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Will pass, since ArgumentException is expected.
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionWhenAsync()
{
// This will throw an exception. But that's expected.
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Passes
[TestMethod]
public void ShouldAllowSyncAction()
{
MyWrapper.Execute(() =>
{
Assert.IsTrue(true);
});
}
}
I have a class which exposes some functionality,
and I want to ensure exceptions will be handled by a custom ErrorHandler class.
Currently I can achieve this by a try / catch statement per each method, and process the exception by the error handler there.
My question is if there is a better way / design pattern to do it.
Code:
public class BasicErrorHandler
{
public void ProcessException(Exception ex)
{
//Does error handling stuff
}
}
public class Manager
{
BasicErrorHandler _errorHandler;
public Manager()
{
_errorHandler = new BasicErrorHandler();
}
public void MethodA()
{
try
{
//Does Something
}
catch(Exception ex)
{
_errorHandler.ProcessException(ex);
}
}
public void MethodB()
{
try
{
//Does Something Else
}
catch(Exception ex)
{
_errorHandler.ProcessException(ex);
}
}
}
In keeping with DRY principles, you could just wrap your try...catch logic into into own method which takes a predicate of the actual work to do:
public class Manager
{
BasicErrorHandler _errorHandler;
public Manager()
{
_errorHandler = new BasicErrorHandler();
}
public void MethodA()
{
DoWork( () => {
// do something interesting here
});
}
public void MethodB()
{
DoWork( () => {
// do something else interesting here
});
}
private void DoWork(Action action)
{
try
{
action();
}
catch(Exception ex)
{
_errorHandler.ProcessException(ex);
}
}
}
I've crafted this quickly and without thinking too much in the implications, but if you want to avoid all the try/catch blocks, you could do something like:
public class BasicErrorHandler
{
public void ProcessException(Exception ex)
{
//Does error handling stuff
}
public void Do(Action act)
{
try
{
act();
}
catch(Exception ex)
{
ProcessException(ex);
}
}
}
And then use it like:
public class Manager
{
BasicErrorHandler _errorHandler;
public Manager()
{
_errorHandler = new BasicErrorHandler();
}
public void MethodA()
{
_errorHandler.Do(() => {
//Does Something
});
}
public void MethodB()
{
_errorHandler.Do(() => {
//Does Something Else
});
}
}
Design patterns are there to solve a problem. Which problem are you trying to solve? What is wrong with the Try Catch blocks?
Only thing I can imagine is you want to have more clean code. Some answers suggest a helper method with an action. Given the helper methods that encapsulate a delegate: Do consider the impact on your stack trace and debugging sessions using these delegates. It might make logging etc more hard to understand.
If your intend is to do separation of concern, I would say If you can't handle it, just don't catch the exception. Let the class invoking the method handle it. If you insist to have a handler in your class, I would suggest Inversion of Control. That way, your class is not in control of determining which class should handle its exceptions.
Rx .net is for You. Advanced error handling gives You the ability to highly customize Your error handling. Check out the pages about that.
For example:
var source = new Subject<int>();
var result = source.Catch<int, TimeoutException>(tx=>Observable.Return(-1));
result.Dump("Catch");
source.OnNext(1);
source.OnNext(2);
source.OnError(new ArgumentException("Fail!"));
You'll get the following output:
Catch-->1
Catch-->2
Catch failed-->Fail!
The number of retries, the handling of how much time a method can take, everything can be configured.
The following is an Aspect oriented method of soling the problem, this makes use of PostSharp to do the weaving.
[Serializable]
public class HandleExceptionsAttribute : OnExceptionAspect {
/// <summary>
/// Initializes a new instance of the <see cref="HandleExceptionsAttribute"/> class.
/// </summary>
public HandleExceptionsAttribute() {
AspectPriority = 1;
}
public override void OnException(MethodExecutionArgs args) {
//Suppress the current transaction to ensure exception is not rolled back
using (var s = new TransactionScope(TransactionScopeOption.Suppress)) {
//Log exception
using (var exceptionLogContext = new ExceptionLogContext()) {
exceptionLogContext.Set<ExceptionLogEntry>().Add(new ExceptionLogEntry(args.Exception));
exceptionLogContext.SaveChanges();
}
}
}
}
[HandleExceptions]
public class YourClass {
}
I'm using the heavily-undocumented Castle dynamic-proxy system. I've managed to make it do almost everything I want, except for one thing: How do you make a proxied method throw an exception instead of returning a value?
public sealed class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (CheckArgs(invocation.Arguments))
{
invocation.ReturnValue = DoRealWork(invocation.Arguments);
}
else
{
invocation.Exception = new InvalidOperationException(); // How?
}
}
}
From the point of view of the proxied object the interceptor is not visible; it simply calls its own virtual method, and DynamicProxy invokes the correct interceptor methods before returning the ReturnValue to the caller.
So if you want to throw an exception just throw it from the interceptor:
if (CheckArgs(invocation.Arguments))
{
invocation.ReturnValue = DoRealWork(invocation.Arguments);
}
else
{
throw new InvalidOperationException();
}
From the point of view of the caller it will be an exception in the called method.
Edit for comment:
Regarding the type of the exception thrown in the generator I have the correct type, not a wrapper:
public interface IDummy
{
string DoSomething();
}
public class Dummy: IDummy {
public virtual string DoSomething()
{
return string.Empty;
}
}
public class MyCustomException : Exception {}
public class CustomIntercept: IInterceptor
{
public void Intercept(IInvocation invocation)
{
throw new MyCustomException();
}
}
internal class Program
{
private static void Main(string[] args)
{
var pg = new ProxyGenerator();
GetValue(pg.CreateInterfaceProxyWithoutTarget<IDummy>(new CustomIntercept()));
GetValue(pg.CreateClassProxy<Dummy>(new CustomIntercept()));
GetValue(pg.CreateClassProxyWithTarget<Dummy>(new Dummy(), new CustomIntercept()));
GetValue(pg.CreateInterfaceProxyWithTarget<IDummy>(new Dummy(), new CustomIntercept()));
}
private static void GetValue(IDummy dummy)
{
try
{
dummy.DoSomething();
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
}
}
}
All four outputs are MyCustomException
Can you make sure that the TargetInvocationException doesn't come from your own code? What version of the DynamicProxy are you using (I'm using the one in Castle.Core 3.2)
I have a system which uses AOP with ContextBoundObject.
This is used to intercept a method call and perform certain operations before and after the function. It all works fine until I make the 'function to be intercepted' async.
I understand that the C# compiler rewrites async methods into a state machine, which returns control to the sink as soon as 'await' is reached
So it continues into the interception and executes the code which is meant to be executed only after the Method.
I can see there is an "AsyncProcessMessage" in IMessageSink, but I can't find a way to invoke it, and I am not sure if it will work in the async/await scenario.
Is there a way to make Async/Await work with the ContextBoundObject? Is using another Aspect Oriented Programming approach the only option here?
The code sample below has the method to be intercepted decorated with the 'Audit' attribute and placed in the AuditFacade which is a ContextBoundObject. The SyncProcessMessage method in the AuditSink has the logic to be executed before and after the method.
[AuditBoundary]
public class AuditFacade : ContextBoundObject
{
[Audit]
public ResponseObject DoSomthing()
{
//Do something
return new ResponseObject();
}
/// <summary>
/// Async Method to be intercepted
/// </summary>
/// <returns></returns>
[Audit]
public async Task<ResponseObject> DoSomthingAysnc()
{
//Do something Async
await Task.Delay(10000);
return new ResponseObject();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class AuditAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class AuditBoundaryAttribute : ContextAttribute
{
public AuditBoundaryAttribute()
: base("AuditBoundary" + Guid.NewGuid().ToString())
{
}
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
ctorMsg.ContextProperties.Add(new AuditProperty());
}
}
public class AuditProperty : IContextProperty, IContributeObjectSink
{
public string Name
{
get { return "AuditProperty"; }
}
public bool IsNewContextOK(Context newCtx)
{
var p = newCtx.GetProperty("AuditProperty") as AuditProperty;
if (p == null)
return false;
return true;
}
public void Freeze(Context newContext)
{
}
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new AuditSink(nextSink);
}
}
public class AuditSink : IMessageSink
{
private IMessageSink nextSink;
public AuditSink(IMessageSink nextSink)
{
this.nextSink = nextSink;
}
public IMessage SyncProcessMessage(IMessage msg)
{
var message = msg as IMethodCallMessage;
IMethodReturnMessage returnMessage = null;
ResponseObject response;
//Some Pre Processing happens here
var newMessage = new MethodCallMessageWrapper(message);
//Invoke the Method to be Audited
returnMessage = nextSink.SyncProcessMessage(newMessage) as IMethodReturnMessage;
response = returnMessage.ReturnValue as ResponseObject;
//Some Post Processing happens here with the "response"
return returnMessage;
}
public IMessageSink NextSink
{
get { return this.nextSink; }
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return nextSink.AsyncProcessMessage(msg, replySink);
}
}
I don't know anything about ContextBoundObject, but I think that AsyncProcessMessage() has nothing to do with async-await and that the following should work using the normal SyncProcessMessage():
Do your preprocessing step.
Invoke the async method.
Add your postprocessing step as a continuation to the returned Task, using ContinueWith() or await.
Return the continuation Task to the caller.
If you're okay with your postprocessing executing on the thread pool, then ContinueWith() is probably simpler. If you need the postprocessing to execute on the original context, use await.
The await version could look like this:
var responseTask = (Task<ResponseObject>)returnMessage.ReturnValue;
Func<Task<ResponseObject>> postProcessTaskFunc = async () =>
{
var response = await responseTask;
// Some Post Processing happens here with the "response"
return response;
}
return new ReturnMessage(postProcessTaskFunc(), …);