1

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.

fhayd
  • 23
  • 5

2 Answers2

0

Is that happening on iOS? Xamarin has some limitations defined by its underlying platforms. Dynamic code is one of them. Avoid using dynamic.

rubo
  • 465
  • 5
  • 11
  • Ah! It crashes on Android, I did not test on iOS yet. But envetually we want to target iOS plateform as well. Thanks for the answer, I will look for another solution – fhayd Mar 26 '19 at 09:35
  • Just announced today: [Xamarin.iOS Interpreter](https://blog.xamarin.com/introducing-xamarin-ios-interpreter/). This may come in handy in the situations like this. – rubo Mar 26 '19 at 17:42
  • Ah, interesting. But you'll loose the AOT benefit in all your app then (I'm not sure how big a problem it is tho). Thanks for letting me know – fhayd Mar 27 '19 at 09:55
0

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.

fhayd
  • 23
  • 5