locked
MVC Error logging with ASYNC Code RRS feed

  • Question

  • User-1188570427 posted

    What is the best way to catch errors that occur with ASYNC code in MVC 5?

    Tuesday, February 26, 2019 9:13 PM

All replies

  • User475983607 posted

    What is the best way to catch errors that occur with ASYNC code in MVC 5?

    Try...Catch.  

    In async methods, you use the provided keywords and types to indicate what you want to do, and the compiler does the rest, including keeping track of what must happen when control returns to an await point in a suspended method. Some routine processes, such as loops and exception handling, can be difficult to handle in traditional asynchronous code. In an async method, you write these elements much as you would in a synchronous solution, and the problem is solved.

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

    Share your code if you need help debugging.  Otherwise, the link above explains the async/await programming pattern.

    Tuesday, February 26, 2019 9:53 PM
  • User1120430333 posted

    tvb2727

    What is the best way to catch errors that occur with ASYNC code in MVC 5?

    You could have a basecontroller that all controllers inherit that catches all unhandled exceptions and logs the error. The exception should be caught by the basecontroller no matter how the  exception was thrown, IMO 

    https://www.code-sample.com/2014/08/base-controller-in-mvc-5.html

    I use a  cutdown version of the code in MVC 5 that just logs the exception and redirects to the Error action in the Home controller that uses the Shared Error view

    namespace MVC
    {
        using System;
    
        public class AppException : Exception
        {
            public AppException(String message) : base(message)
            { }
    
            public string Type { get; set; }
            public string StackTrace { get; set; }
            public string InnerException { get; set; }
            public AppException(String message, Exception inner) : base(message, inner) { }
        }
    
    }
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using log4net;
    
    namespace MVC.Controllers
    {
        public abstract partial class BaseController : Controller
        {
            private ILog _logger;
    
            protected BaseController()
            {
                _logger =  LogManager.GetLogger(typeof(BaseController));
            }
    
            protected override void OnException(ExceptionContext filterContext)
            {
                AppException appException = new AppException(Convert.ToString(filterContext.Exception))
                {
                    Type = filterContext.GetType().ToString(),
                    StackTrace = filterContext.Exception.StackTrace,
                    Source = filterContext.Exception.Source,
                    InnerException = Convert.ToString(filterContext.Exception.InnerException)
                };
    
                _logger.Error(appException.ToString());
    
                Server.ClearError();
    
                RedirectToControllers("Home", "Error");
            }
    
            private void RedirectToControllers(string control, string action)
            {
                var routeData = new RouteData();
    
                routeData.Values["controller"] = control;
    
                routeData.Values["action"] = action;
    
                IController controller = new HomeController();
    
                ((IController) controller).Execute(new RequestContext(
                    new HttpContextWrapper(System.Web.HttpContext.Current), routeData));
            }
        }
    }
    
    
    using System.Web.Mvc;
    using MVC.Models;
    
    namespace MVC.Controllers
    {
        [Authorize]
        public class StudentsController : BaseController  
        {
            private IStudentModels studmods;
            public StudentsController(IStudentModels studentModels)
            {
                studmods = studentModels;
            }
    
            // GET: Students
    
            [AllowAnonymous]
            public ActionResult Index()
            {
                return View(studmods.GetStudents());
            }
    
            //[AllowAnonymous]
            public ActionResult Details(int id = 0)
            {
                return id == 0 ? null : View(studmods.GetStudentById(id));
            }
            public ActionResult Create()
            {
                return View(studmods.Create());
            }
    
            [HttpPost]
            public ActionResult Create(StudentViewModels.Student student)
            {
                if (ModelState.IsValid)
                {
                    studmods.Create(student);
                    return RedirectToAction("Index");
                }
    
                return View(student);
            }
    
            public ActionResult Edit(int id = 0)
            {
                return id == 0 ? null : View(studmods.Edit(id));
            }
    
            [HttpPost]
            public ActionResult Edit(StudentViewModels.Student student)
            {
                if (ModelState.IsValid)
                {
                    studmods.Edit(student);
                    return RedirectToAction("Index");
                }
    
                return View(student);
            }
    
            public ActionResult Delete(int id = 0 )
            {
                if (id > 0) studmods.Delete(id);
                
                return RedirectToAction("Index");
             }
        }
    }



    Tuesday, February 26, 2019 10:04 PM
  • User-1188570427 posted

    tvb2727

    What is the best way to catch errors that occur with ASYNC code in MVC 5?

    Try...Catch.  

    In async methods, you use the provided keywords and types to indicate what you want to do, and the compiler does the rest, including keeping track of what must happen when control returns to an await point in a suspended method. Some routine processes, such as loops and exception handling, can be difficult to handle in traditional asynchronous code. In an async method, you write these elements much as you would in a synchronous solution, and the problem is solved.

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

    Share your code if you need help debugging.  Otherwise, the link above explains the async/await programming pattern.

    Thanks mgebard.

    So what if I have this the below async method:

            public async Task<ActionResult> CreateOrganization(CreateOrganizationViewModel createOrganizationModel)
            {
                if (createOrganizationModel == null)
                {
                    throw new ArgumentNullException(nameof(createOrganizationModel));
                }
    
                var currentUserId = User.Identity;
    
                var newlyCreatedOrganizationId = await Utilities.CreateOrganization(createOrganizationModel, currentUserId ).ConfigureAwait(false);
    
                return Json(new { success = true, newlyCreatedOrganizationId });
            }

    Is there a way to put an attribute on top to catch errors for us or since it is an async method, just put a try catch around it and re-throw the error after we log it?

    Maybe as such: 

            public async Task<ActionResult> CreateOrganization(CreateOrganizationViewModel createOrganizationModel)
            {
                try
                {
                    if (createOrganizationModel == null)
                    {
                        throw new ArgumentNullException(nameof(createOrganizationModel));
                    }
    
                    var currentUserId = User.Identity;
    
                    var newlyCreatedOrganizationId = await Utilities.CreateOrganization(createOrganizationModel, currentUserId).ConfigureAwait(false);
    
                    return Json(new { success = true, newlyCreatedOrganizationId });
                }
                catch (Exception ex)
                {
                    // log my error to the database or Nlog here...
    
                    throw;
                }
            }

    Wednesday, February 27, 2019 2:29 PM
  • User-1188570427 posted

    tvb2727

    What is the best way to catch errors that occur with ASYNC code in MVC 5?

    You could have a basecontroller that all controllers inherit that catches all unhandled exceptions and logs the error. The exception should be caught by the basecontroller no matter how the  exception was thrown, IMO 

    https://www.code-sample.com/2014/08/base-controller-in-mvc-5.html

    I use a  cutdown version of the code in MVC 5 that just logs the exception and redirects to the Error action in the Home controller that uses the Shared Error view

    namespace MVC
    {
        using System;
    
        public class AppException : Exception
        {
            public AppException(String message) : base(message)
            { }
    
            public string Type { get; set; }
            public string StackTrace { get; set; }
            public string InnerException { get; set; }
            public AppException(String message, Exception inner) : base(message, inner) { }
        }
    
    }
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using log4net;
    
    namespace MVC.Controllers
    {
        public abstract partial class BaseController : Controller
        {
            private ILog _logger;
    
            protected BaseController()
            {
                _logger =  LogManager.GetLogger(typeof(BaseController));
            }
    
            protected override void OnException(ExceptionContext filterContext)
            {
                AppException appException = new AppException(Convert.ToString(filterContext.Exception))
                {
                    Type = filterContext.GetType().ToString(),
                    StackTrace = filterContext.Exception.StackTrace,
                    Source = filterContext.Exception.Source,
                    InnerException = Convert.ToString(filterContext.Exception.InnerException)
                };
    
                _logger.Error(appException.ToString());
    
                Server.ClearError();
    
                RedirectToControllers("Home", "Error");
            }
    
            private void RedirectToControllers(string control, string action)
            {
                var routeData = new RouteData();
    
                routeData.Values["controller"] = control;
    
                routeData.Values["action"] = action;
    
                IController controller = new HomeController();
    
                ((IController) controller).Execute(new RequestContext(
                    new HttpContextWrapper(System.Web.HttpContext.Current), routeData));
            }
        }
    }
    
    
    using System.Web.Mvc;
    using MVC.Models;
    
    namespace MVC.Controllers
    {
        [Authorize]
        public class StudentsController : BaseController  
        {
            private IStudentModels studmods;
            public StudentsController(IStudentModels studentModels)
            {
                studmods = studentModels;
            }
    
            // GET: Students
    
            [AllowAnonymous]
            public ActionResult Index()
            {
                return View(studmods.GetStudents());
            }
    
            //[AllowAnonymous]
            public ActionResult Details(int id = 0)
            {
                return id == 0 ? null : View(studmods.GetStudentById(id));
            }
            public ActionResult Create()
            {
                return View(studmods.Create());
            }
    
            [HttpPost]
            public ActionResult Create(StudentViewModels.Student student)
            {
                if (ModelState.IsValid)
                {
                    studmods.Create(student);
                    return RedirectToAction("Index");
                }
    
                return View(student);
            }
    
            public ActionResult Edit(int id = 0)
            {
                return id == 0 ? null : View(studmods.Edit(id));
            }
    
            [HttpPost]
            public ActionResult Edit(StudentViewModels.Student student)
            {
                if (ModelState.IsValid)
                {
                    studmods.Edit(student);
                    return RedirectToAction("Index");
                }
    
                return View(student);
            }
    
            public ActionResult Delete(int id = 0 )
            {
                if (id > 0) studmods.Delete(id);
                
                return RedirectToAction("Index");
             }
        }
    }



    Thanks for this example DA924. I'll check it out.

    Wednesday, February 27, 2019 2:41 PM
  • User475983607 posted

    Is there a way to put an attribute on top to catch errors for us or since it is an async method, just put a try catch around it and re-throw the error after we log it?

    The standard MVC docs cover how to handle errors in MVC but either method, try...catch or attributes, will work. 

    Write code in a base controller OnException or an MVC action filter.   I recommend doing an internet search and finding a solution that fits your needs as exception handling is a rather mature subject in ASP.NET.

    MVC also comes with a [HandleError] attribute to handle custom error pages.

    https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.handleerrorattribute?view=aspnet-mvc-5.2

    Wednesday, February 27, 2019 2:46 PM