'.NET CORE, how to get file location for an exception in a web app, in middleware?

I've seen this question:

How can I get the line number and file name from an exception in net Core?

And tried the code, but it always says line 0 and no file name is supplied when called in middleware for all the frames in the exception.

I have an unhandled exception middleware I have written and the main part looks like this:

    public async Task Invoke(HttpContext context)
    {
        var num = 0;
        Exception exceptionThatOccurred = null;

        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            if (context.Response.StatusCode != (int) HttpStatusCode.OK)
            {
                await _next(context);
                return;
            }
                
            num = 1;
            exceptionThatOccurred = ex;
        }
        if (num == 1)
        {
            //do stuff to process the error

Everything works fine, any unhandled exception processes the error. But in trying to get the file name, method, and line numbers inside that:

            var trace = new StackTrace(exceptionThatOccurred, true);
            var frames = trace.GetFrames();
            var frameDataMessages = new List<string>();
            foreach (var frame in frames)
            {
                var lineNumber = frame.GetFileLineNumber();
                var fileName = frame.GetFileName();
                var methodName = frame.GetMethod();
                var errorFileLocatonMessage = "<strong>" + fileName + "</strong> on <strong>line " + lineNumber + "</strong> in method <strong>" + methodName + "</strong>.";
                frameDataMessages.Add(errorFileLocatonMessage);
            }

I get an output like this:

on line 0 in method Int32 SaveChanges().

on line 0 in method Void Commit().

on line 0 in method Void Commit().

on line 0 in method Void Save(TClass, System.String, Boolean, Boolean).

on line 0 in method Microsoft.AspNetCore.Mvc.IActionResult Index().

on line 0 in method System.Object lambda_method(System.Runtime.CompilerServices.Closure, > System.Object, System.Object[]).

on line 0 in method Void MoveNext().

on line 0 in method Void Throw().

on line 0 in method Void MoveNext().

on line 0 in method Void Throw().

on line 0 in method Void Rethrow(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext).

on line 0 in method System.Threading.Tasks.Task Next(State ByRef, Scope ByRef, System.Object ByRef, Boolean ByRef).

on line 0 in method Void MoveNext().

on line 0 in method Void Throw(). on line 0 in method Void Rethrow(Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext).

on line 0 in method System.Threading.Tasks.Task Next(State ByRef, Scope ByRef, System.Object ByRef, Boolean ByRef).

on line 0 in method Void MoveNext().

on line 0 in method Void Throw().

on line 0 in method VoidHandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task).

on line 0 in method Void GetResult().

on line 0 in method Void MoveNext().

Now it looks like I should only be getting the first frame where the relevant info is, and the stack trace shows something like this:

at MyFramework.Repositories.MyDbContext.SaveChanges() at MyFramework.Repositories.EntityFrameworkQueryable'4.Commit() at MyFramework.Repositories.EntityRepository'3.Commit() at MyFramework.Services.EntityService'2.Save(TClass entity, String action) at MyWebsite.Controllers.TestsController.Index() at lambda_method(Closure , Object , Object[] ) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__27.MoveNext() --- End of stack trace from previous location where exception was thrown ---

The top level line where the exception is thrown is:

var test = new Equipment();
test.Id = Guid.Empty;
_equipmentService.Save(test, "Testing errors");

In TestsController.cs, line 60, method Index. But how can I get that info out of the frame data?



Solution 1:[1]

UPDATE

enter image description here

I use below Global Error Handler to capture the exception, please try it. And this is a sample code, the classification of Exception is not very comprehensive, you can further optimize it if you need it.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using webapi.Helpers;

namespace webapi.Middleware
{
    public class ErrorHandlingMiddleware
    {
        private readonly RequestDelegate next;

        public ErrorHandlingMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
            }
            catch (Exception ex)
            {
                await HandleException(context, ex);
            }
            finally {
                await Handle400Exception(context);
            }
        }

        private static Task HandleException(HttpContext context, Exception ex)
        {
            var response = context.Response;
            response.ContentType = "application/json";
            string DetailedMsg = string.Empty;
            switch (ex)
            {
                case AppException e:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    break;
                case KeyNotFoundException e:
                    response.StatusCode = (int)HttpStatusCode.NotFound;
                    break;
                case System.AggregateException e:
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    break;
                case FileNotFoundException e:
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    DetailedMsg = AnalyzeException(e);
                    break;
                default:
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    break;
            }
            var result = JsonSerializer.Serialize(new { message = ex?.Message, detailedMsg= DetailedMsg.Equals(string.Empty)?"Nothing":DetailedMsg });
            return context.Response.WriteAsync("Status Code: 500; Internal Server Error, Message = " + result);
        }
        private static Task Handle400Exception(HttpContext context)
        {
            var response = context.Response;
            response.ContentType = "application/json";
            string Message = string.Empty;
            if (response.StatusCode == 401) {
                Message = "unauthorize";
            }
            if (response.StatusCode == 404)
            {
                Message = "Not found, pls check url";
            }
            var result = JsonSerializer.Serialize(new { message = Message });
            return context.Response.WriteAsync("Status Code: 400; Message = " + result);
        }
        private static string AnalyzeException(Exception e)
        {
            var trace = new StackTrace(e, true);
            var stackFrame = trace.GetFrames()[trace.FrameCount - 1];
            var reflectedType = trace.GetFrames()[trace.FrameCount - 1].GetMethod().ReflectedType;
            var additionalInformation = new AdditionalInformation();
            if (reflectedType != null)
            {
                additionalInformation = new AdditionalInformation()
                {
                    Column = stackFrame.GetFileColumnNumber(),
                    Line = stackFrame.GetFileLineNumber(),
                    MethodName = reflectedType.FullName,
                    File = stackFrame.GetFileName()
                };

            }
            return "";
        }
        public class AdditionalInformation { 
            public int Column { get; set; }
            public int Line { get; set; }
            public string MethodName { get; set; }
            public string File { get; set; }
        }
    }

}

PREVIOUS

Try below code and it works for me. If you still want use GetFrames(), you can refer below code.

var stackFrame = trace.GetFrames()[trace.FrameCount - 1];
var reflectedType = trace.GetFrames()[trace.FrameCount - 1].GetMethod().ReflectedType;

Test Result

enter image description here

enter image description here

My Test Code

private static string AnalyzeException(Exception e)
{
    var trace = new StackTrace(e, true);
    var stackFrame = trace.GetFrame(trace.FrameCount-1);
    var reflectedType = trace.GetFrame(trace.FrameCount-1).GetMethod().ReflectedType;
    var additionalInformation = new AdditionalInformation();
    if (reflectedType != null)
    {
        additionalInformation = new AdditionalInformation()
        {
            Column = stackFrame.GetFileColumnNumber(),
            Line = stackFrame.GetFileLineNumber(),
            MethodName = reflectedType.FullName,
            File = stackFrame.GetFileName()
        };

    }
    return "";
}
public class AdditionalInformation { 
    public int Column { get; set; }
    public int Line { get; set; }
    public string MethodName { get; set; }
    public string File { get; set; }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1