#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
)