You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中OnExceptionAsync如何获取抛出异常的原始方法名称

问题原因

异步/await方法在编译阶段会被编译器转换为状态机类,抛出异常时栈顶的方法实际是状态机的MoveNext方法,并非你编写的原始业务方法。

解决方案

你可以通过识别状态机类型的元数据,反向解析出原始的业务方法名和类名,以下是可直接复用的实现:

第一步:实现栈帧解析扩展方法

using System.Reflection;
using System.Diagnostics;
using System.Runtime.CompilerServices;

public static class StackTraceExtensions
{
    public static MethodBase GetOriginalMethod(this StackFrame stackFrame)
    {
        var method = stackFrame.GetMethod();
        if (method == null) return null;
        
        // 判断是否是异步状态机的MoveNext方法
        var declaringType = method.DeclaringType;
        if (declaringType == null) return method;
        
        // 状态机类会实现IAsyncStateMachine接口,且是编译器生成的类型
        if (typeof(IAsyncStateMachine).IsAssignableFrom(declaringType) 
            && Attribute.IsDefined(declaringType, typeof(CompilerGeneratedAttribute)))
        {
            // 状态机类名格式通常是 <原始方法名>d__x,提取尖括号中间的原始方法名
            var declaringTypeName = declaringType.Name;
            if (declaringTypeName.StartsWith('<') && declaringTypeName.Contains('>'))
            {
                var originalMethodName = declaringTypeName[1..declaringTypeName.IndexOf('>')];
                // 状态机类是原始方法所属类的嵌套类,取外层类型即为原始类
                var originalType = declaringType.DeclaringType;
                if (originalType != null)
                {
                    var originalMethod = originalType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
                        .FirstOrDefault(m => m.Name == originalMethodName);
                    return originalMethod ?? method;
                }
            }
        }
        return method;
    }
}

第二步:修改异常过滤器中的调用逻辑

public class ExceptionFilter : ExceptionFilterAttribute
{
    public override Task OnExceptionAsync(HttpActionExecutedContext context, CancellationToken cancellationToken)
    {
        var stackTrace = new StackTrace(context.Exception, true);
        MethodBase originalMethod = null;
        // 遍历栈帧找到第一个非编译器生成的原始业务方法
        for (int i = 0; i < stackTrace.FrameCount; i++)
        {
            var frame = stackTrace.GetFrame(i);
            var method = frame.GetOriginalMethod();
            if (method != null && !Attribute.IsDefined(method.DeclaringType, typeof(CompilerGeneratedAttribute)))
            {
                originalMethod = method;
                break;
            }
        }
        
        // 获取最终的原始类名、方法名
        var methodName = originalMethod?.Name ?? "UnknownMethod";
        var className = originalMethod?.DeclaringType?.FullName ?? "UnknownClass";
        
        // 后续业务逻辑
        Logger.LogError(context.Exception, $"类[{className}]的方法[{methodName}]抛出异常:{context.Exception.Message}");
        
        context.Response = context.Request.CreateResponse(HttpStatusCode.OK, res);
        return Task.CompletedTask;
    }
}

注意事项

  • 如果发布模式开启了代码优化、裁剪或者混淆,可能会导致状态机类名格式变化、解析失败,生产环境使用前建议先做兼容性测试
  • 若需要更高的解析成功率,可以调整遍历逻辑,跳过系统命名空间下的栈帧,取第一个业务代码对应的栈帧即可

内容的提问来源于stack exchange,提问作者Shahzad Ahamad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 17:45:02