pamidur / aspect-injector

AOP framework for .NET (c#, vb, etc)
Apache License 2.0
745 stars 112 forks source link

Custom Exception problem #113

Closed kargencW closed 4 years ago

kargencW commented 4 years ago

this is my aspect class

[Aspect(Scope.PerInstance)]
    [Injection(typeof(AspectLogForMethodArountExceptionAndLog))]
    public class AspectLogForMethodArountExceptionAndLog : Attribute
    {
        private static readonly MethodInfo _asyncHandler = typeof(AspectLogForMethodArountExceptionAndLog).GetMethod(nameof(AspectLogForMethodArountExceptionAndLog.WrapAsync), BindingFlags.NonPublic | BindingFlags.Static);
        private static readonly MethodInfo _syncHandler = typeof(AspectLogForMethodArountExceptionAndLog).GetMethod(nameof(AspectLogForMethodArountExceptionAndLog.WrapSync), BindingFlags.NonPublic | BindingFlags.Static);
        private static readonly Type _voidTaskResult = Type.GetType("System.Threading.Tasks.VoidTaskResult");

        private ILogger logger;

        [Advice(Kind.Around)]
        public object Handle(
            [Argument(Source.Target)] Func<object[], object> target,
            [Argument(Source.Arguments)] object[] args,
            [Argument(Source.Name)] string name,
            [Argument(Source.ReturnType)] Type retType
            )
        {
            logger = PrncLogFactory.GetLogger(target.Method.DeclaringType);
            var operationName = $"{target.Method.DeclaringType.FullName}:{name}";

            if (typeof(Task).IsAssignableFrom(retType))
            {
                var syncResultType = retType.IsConstructedGenericType ? retType.GenericTypeArguments[0] : _voidTaskResult;
                var tgt = target;
               return _asyncHandler.MakeGenericMethod(syncResultType).Invoke(this, new object[] { tgt, args, operationName, logger }); 
            }
            else
            {
                retType = retType == typeof(void) ? typeof(object) : retType;
                return _syncHandler.MakeGenericMethod(retType).Invoke(this, new object[] { target, args, operationName, logger });
            }
        }

        private static T WrapSync<T>(Func<object[], object> target, object[] args, string name, ILogger logger)
        {
            try
            {
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Debug, null, $"Method `{name}` started.");
                var result = (T)target(args);
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Debug, null, $"Method `{name}` finished.");
                return result;
            }
            catch (Exception e)
            {
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Error, e, $"Method `{name}` throws {e.GetType()} exception.");
                throw e;
            }
        }

        private static async Task<T> WrapAsync<T>(Func<object[], object> target, object[] args, string name, ILogger logger)
        {
            try
            {
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Debug, null, $"Method `{name}` started.");
                var result = await ((Task<T>)target(args)).ConfigureAwait(false);
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Debug, null, $"Method `{name}` finished.");
                return result;
            }
            catch (Exception e)
            {
                logger.WriteLog(Microsoft.Extensions.Logging.LogLevel.Error, e, $"Method `{name}` throws {e.GetType()} exception.");
                throw e;
            }
        }
    }

and this is test code

[AspectLogForMethodArountExceptionAndLog]
    class Program
    {

        static void Main(string[] args)
        {
            try
            {
                method2();

            }
            catch (customexception)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }

        }

        static void method2()
        {
            throw new customexception();
            //System.IO.File.AppendAllLines("f:\\test\\log.text", new string[] { "hede" }); // will be throw because no F:\ disk
        }

    }

    class customexception : ApplicationException
    {

    }

i never catch customexception in main try catch block, how can i solve this case?

pamidur commented 4 years ago

Hi, for this you want to unwrap InvokationExceptions, like this:

[Advice(Kind.Around)]
        public object Handle(
            [Argument(Source.Target)] Func<object[], object> target,
            [Argument(Source.Arguments)] object[] args,
            [Argument(Source.Name)] string name,
            [Argument(Source.ReturnType)] Type retType
            )
        {
            var operationName = $"{target.Method.DeclaringType.FullName}:{name}";

            if (typeof(Task).IsAssignableFrom(retType))
            {
                var syncResultType = retType.IsConstructedGenericType ? retType.GenericTypeArguments[0] : _voidTaskResult;
                var tgt = target;
                return InvokeWrapper(_asyncHandler.MakeGenericMethod(syncResultType), target, args, operationName);
            }
            else
            {
                retType = retType == typeof(void) ? typeof(object) : retType;
                return InvokeWrapper(_syncHandler.MakeGenericMethod(retType), target, args, operationName);
            }
        }

        private object InvokeWrapper(MethodInfo method, Func<object[], object> target, object[] args, string opname)
        {
            try
            {
                return method.Invoke(this, new object[] { target, args, opname });
            }
            catch (TargetInvocationException te)
            {
                throw te.InnerException;
            }
        }
kargencW commented 4 years ago
 class Program
    {

        static void Main(string[] args)
        {
            try
            {
                try
                {
                    throw new customexception();

                }
                catch (customexception ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }

    }

    class customexception : ApplicationException
    {

    }

this also cannot work, why inner customexception not working, any idea ?

pamidur commented 4 years ago

Because they get wrapped by method.Invoke(). See my comment above

kargencW commented 4 years ago

catch (TargetInvocationException te) { throw te.InnerException; }

solve my problem, thank you very much