MichaelCleverly
MichaelCleverly

Reputation: 2543

Asynchronous error handling in MVC 5 filters

I've made a custom filter attribute extending the HandleErrorAttribute, that catches all exceptions.

My entire application is built around async architechture, throughout the stack. This means, that I somehow have to use the OnException(ExceptionContext contect) override asynchronously. Something like this, would be how I imagined it (Simplified for readability):

public async override void OnException(ExceptionContext context)
{
    base.OnException(context);
    var logId = await LogException(e, httpContext); //LogException calls an async method and returns the ID of the exception, after it has been saved to the database. I this ID in the filterContext, which is why I need the result of the method
}

However, this would result in InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle.

I cannot simply call .Result on the task when called synchronously, as this would cause deadlocks.

So I guess my question is: How do I create a filter in MVC5, that can log errors asynchronously?

Upvotes: 2

Views: 1565

Answers (2)

Alex Pashkin
Alex Pashkin

Reputation: 301

I suggest you to change approach and implement own ExceptionLogger because it's ExceptionLogger job to log exceptions

We provide two new user-replaceable services, IExceptionLogger and IExceptionHandler, to log and handle unhandled exceptions. The services are very similar, with two main differences: We support registering multiple exception loggers but only a single exception handler. Exception loggers always get called, even if we’re about to abort the connection. Exception handlers only get called when we’re still able to choose which response message to send. Both services provide access to an exception context containing relevant information from the point where the exception was detected, particularly the HttpRequestMessage, the HttpRequestContext, the thrown exception and the exception source.

Here the sample code

public class ExceptionLogger : IExceptionLogger
{
    public virtual Task LogAsync(ExceptionLoggerContext context, 
                             CancellationToken cancellationToken)
   {


    return MyLogger.Log(context, cancellationToken);
   }   
}

Upvotes: 0

usr
usr

Reputation: 171236

Since error handling usually is not hot code there are no performance concerns here. Therefore, do what's most convenient to you.

For example, you can block on the task returned from LogException. There are multiple ways you can reliably avoid a deadlock. A simple way is Task.Run(() => LogException()).Wait();. This works because the task body runs without a synchronization context.

You also can add ConfigureAwait(false) inside of LogException but you cannot miss a single place. That makes this brittle.

Upvotes: 4

Related Questions