ASP.NET C # Перехват всех исключений в классе

#c# #asp.net #exception-handling

#c# #asp.net #исключение

Вопрос:

Я знаю, что это не тот способ сделать это, и это совсем не чисто. Мне просто интересно, возможно ли это.

Если у меня есть класс с кучей методов

 public class Foo {

   methodA() {}

   methodB() {}

   methodC() {}

}
  

Возможно ли перехват всех исключений, которые могут возникнуть, без необходимости писать try / catch в каждом методе?

Комментарии:

1. Напишите блок try, в котором вы выполняете / могли бы вызывать эти методы, не делайте этого в определениях методов.

2. Почему бы тебе не попробовать code.google.com/p/elmah он будет регистрировать все ошибки для вас в очень хорошей базе данных.

3. Но если я вызываю эти методы несколько сотен раз в дюжине разных классов, я все равно в конечном итоге пишу try / catches во всех этих методах.

Ответ №1:

Да, это так. Самым простым способом был бы атрибут для этого класса, подобный этому:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HandleErrorAttribute : FilterAttribute, IExceptionFilter
{

    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (filterContext.ExceptionHandled)
        {
            return;
        }

        var exception = filterContext.Exception;

        // that need to be your current request object. In this case I use a custom one so I must fetch it from the items collection of the current request, where I had stored it before.
        var request = filterContext.HttpContext.Items[Request.RequestKey] as Request;

        if (request != null)
        {
            // overwrite ErrorResponse with a response object of your choice or write directly to the filterContext.HttpContext.Response
            var errorResponse = new ErrorResponse(request, exception); 
            errorResponse.Write(filterContext.HttpContext.Response);
            filterContext.ExceptionHandled = true;
        }
    }
}

// Or a just slightly modified version of the default ASP.Net MVC HandleError Attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class CustomHandleErrorAttribute : FilterAttribute, IExceptionFilter
    {
        // Fields
        private const string _defaultView = "Error";
        private string _master;
        private readonly object _typeId = new object();
        private string _view;

        // Methods
        public virtual void OnException(ExceptionContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (!filterContext.IsChildAction amp;amp; (!filterContext.ExceptionHandled amp;amp; filterContext.HttpContext.IsCustomErrorEnabled))
            {
                Exception innerException = filterContext.Exception;
                if ((new HttpException(null, innerException).GetHttpCode() == 500))
                {
                    string controllerName = (string)filterContext.RouteData.Values["controller"];
                    string actionName = (string)filterContext.RouteData.Values["action"];
                    HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
                    ViewResult result = new ViewResult();
                    result.ViewName = this.View;
                    result.MasterName = this.Master;
                    result.ViewData = new ViewDataDictionary<HandleErrorInfo>(model);
                    result.TempData = filterContext.Controller.TempData;
                    filterContext.Result = resu<
                    filterContext.ExceptionHandled = true;
                    filterContext.HttpContext.Response.Clear();
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                }
            }
        }

        public string Master
        {
            get
            {
                return (this._master ?? string.Empty);
            }
            set
            {
                this._master = value;
            }
        }

        public override object TypeId
        {
            get
            {
                return this._typeId;
            }
        }

        public string View
        {
            get
            {
                if (string.IsNullOrEmpty(this._view))
                {
                    return "Error";
                }
                return this._view;
            }
            set
            {
                this._view = value;
            }
        }
    }
  

Использование (непроверенное, потому что я использовал его в контексте контроллера, который уже реализует все необходимые интерфейсы)

 [HandleErrorAttribute]
public class Foo : IExceptionFilter // (I am not sure about this one IActionFilter)
{

    public void MethodA() 
    {
        // body
    }

    public void MethodB() 
    {
        // body
    }

    public void MethodC()
    {
        // body
    }

}
  

Или вы можете сделать что-то вроде этого:

 public class ExecuteHelper
{
    public static void Catch(Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            // Do what you want
        }
    }
}
  

И использует это в теле функции:

 public void Foo(string something)
{
    ExecuteHelper.Catch(() =>
    {
        // Do something with something or without something
    });
}
  

Комментарии:

1. Если я не ошибаюсь, это та же идея, что и атрибут HandleError в System.Web.Mvc.

2. @Matthias Нет проблем! Но не используйте это как единственную реализацию для обработки исключений, иначе вы застрянете внутри отладчика. Обрабатывайте исключения в точке, где они возникают, и при необходимости учитывайте повторный запуск.

3. @sra: Хорошо, я попытался реализовать ваше первое решение сегодня в качестве теста, но ‘Request’ и ‘ErrorResponse’ неизвестны (не удается разрешить символ). Также, если это компилируется правильно. как бы я это использовал? Как в MVC для аннотации данных: [HandleErrorAttribute] methodA(); ?

4. Хорошо, я обновил сообщение. Взгляните и дайте мне обратную связь, если вам нужна дополнительная информация.

5. @sra Могу ли я в любом случае использовать глобальную обработку исключений в форме аннотирующего класса, но без подключения MVC?

Ответ №2:

Вы могли бы написать функцию более высокого порядка для обработки исключений, что сделало бы ее несколько более чистой.

 private T FooExceptionHandler(Func<T> function)
{
   try
   {
      return function();
   }
   catch
   {
      //handle it
   }
}
  

Вы можете заменить Func на Action, если у вас нет возвращаемого значения.
Вы можете использовать это двумя способами, вне функции:

 FooExceptionHandler(MethodA);
  

или внутри каждой функции:

 MethodA()
{
   return FooExceptionHandler(()=>
      {
         //Function body goes here
      });
}
  

Комментарии:

1. Не могли бы вы обновить свой ответ, чтобы показать код, как я бы вызвал метод из вопроса OP, используя эту функцию более высокого порядка, спасибо.

2. «вы можете заменить Func на Action» < — не могли бы вы подробнее остановиться на этом?

Ответ №3:

Нет, такого не существует. try / catch блоки могут встречаться только в методах. Однако вы могли бы использовать какой-нибудь фреймворк AOP для автоматической генерации этих блоков. postcrap — довольно легкий компонент AOP.

Ответ №4:

Вы можете обработать это в функции, которая вызывает эти методы…

Ответ №5:

По умолчанию в ASP.NET вы можете переопределить OnError метод, а затем выполнить Server.ClearError удаление исключения.

Я не уверен, применимо ли это к вашему случаю (работает только в Page и Global.asax )