尝试捕获块中的代码重复
本文关键字:代码 | 更新日期: 2023-09-27 18:35:29
有没有更好的方法来捕获异常?我似乎复制了很多代码。基本上在每个控制器中,我都有一个 catch 语句来做到这一点:
try
{
Do something that might throw exceptions.
}
catch (exception ex)
{
Open database connection
Save exception details.
If connection cannot be made to the database save exception in a text file.
}
我有 4 个控制器,每个控制器中有大约 5-6 个操作方法,这是大量的代码重复。如何减少上面 try catch 语句中的行数?
你可以在这里使用扩展方法。
在新类中创建扩展方法。
public static class ExtensionMethods
{
public static void Log(this Exception obj)
{
// log your Exception here.
}
}
并像这样使用它:
try
{
}
catch (Exception obj)
{
obj.Log();
}
您不需要在每个方法上都放置 try/catch 块。这既乏味又痛苦!相反,您可以使用 Global.asax 的Application_Error事件来记录异常。下面的代码是示例实现,可用于捕获 Web 应用程序中发生的异常。
protected void Application_Error(object sender, EventArgs e)
{
var error = Server.GetLastError();
if (!string.IsNullOrWhiteSpace(error.Message))
{
//do whatever you want if exception occurs
Context.ClearError();
}
}
我还想强调的是,"已处理异常",尤其是尝试在大多数方法上放置 try/catch 块,是"IIS/ASP.NET 应用程序的三大无声性能杀手"之一,如本博客中所述 http://mvolo.com/fix-the-3-high-cpu-performance-problems-for-iis-aspnet-apps/
您要执行的操作称为横切关注点。您正在尝试记录代码中任何位置发生的任何错误。
ASP.NET MVC横切关注点可以通过使用筛选器来实现。筛选器是可以全局应用于控制器或方法的属性。它们在操作方法执行之前或之后运行。
您有几种类型的筛选器:
- 授权过滤器,它们运行以检查是否允许用户访问资源。 操作筛选器
- ,这些筛选器在操作方法执行之前和之后运行。 结果筛选器,
- 这些筛选器可用于更改操作方法的结果(例如,向输出添加一些额外的 HTMl)
- 每当引发异常时,都会运行异常筛选器。
在您的情况下,您正在寻找异常过滤器。仅当操作方法中发生异常时,才会运行这些筛选器。您可以全局应用筛选器,以便它自动针对任何控制器中的所有异常运行。您还可以在某些控制器或方法上专门使用它。
在 MSDN 文档中,您可以找到如何实现自己的筛选器。
就个人而言,由于我非常不喜欢try
/catch
块,因此我使用了一个static
Try
类,其中包含将操作包装在可重用的try
/catch
块中的方法。前任:
public static class Try {
bool TryAction(Action pAction) {
try {
pAction();
return true;
} catch (Exception exception) {
PostException(exception);
return false;
}
}
bool TryQuietly(Action pAction) {
try {
pAction();
return true;
} catch (Exception exception) {
PostExceptionQuietly(exception);
return false;
}
}
bool TrySilently(Action pAction) {
try {
pAction();
return true;
} catch { return false; }
}
// etc... (lots of possibilities depending on your needs)
}
我在应用程序中使用了一个名为 ExceptionHandler 的特殊类,在静态类中,我有一些方法来处理应用程序的异常。它让我有机会集中异常处理。
public static class ExceptionHandler
{
public static void Handle(Exception ex, bool rethrow = false) {...}
....
}
在该方法中,您可以记录异常,重新抛出它,将其替换为另一种异常等。
我在这样的尝试/捕获中使用它
try
{
//Do something that might throw exceptions.
}
catch (exception ex)
{
ExceptionHandler.Handle(ex);
}
正如 Wouter de Kort 在他的回答中正确指出的那样,这是一个横切关注点,所以我把这个类放在我的应用层中,并将其用作服务。如果将类定义为接口,则可以在不同的场景中使用不同的实现。
您也可以使用单例模式:
sealed class Logger
{
public static readonly Logger Instance = new Logger();
some overloaded methods to log difference type of objects like exceptions
public void Log(Exception ex) {}
...
}
和
Try
{
}
Catch(Exception ex)
{
Logger.Instance.Log(ex);
}
编辑有些人出于合理的理由不喜欢单例,而不是单例,我们可以使用一些 DI:
class Controller
{
private ILogger logger;
public Controller(ILogger logger)
{
this.logger = logger;
}
}
并使用一些 DI 库,它将 ILogger 的一个实例注入到控制器中。
建议一般解决方案的答案,但是我想指出另一个适用于MVC的答案。如果你有一个通用的控制器基础(无论如何你应该这样做,这是一个最佳实践IMO)。您可以简单地重写 OnException 方法:
public class MyControllerBase : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
DoSomeSmartStuffWithException(filterContext.Exception);
base.OnException(filterContext);
}
}
然后简单地从公共基础继承普通控制器而不是控制器
public class MyNormalController : MyControllerBase
{
...
如果你喜欢这个,你可以查看控制器类的其他方便的虚拟方法,它有很多。
在 ASP .NET MVC 中,您可以实现自己的HandleErrorAttribute
来捕获所有控制器中发生的所有异常:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
// Open database connection
// Save exception details.
// If connection cannot be made to the database save exception in a text file.
}
}
然后注册此筛选器:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
}
}
当然,在应用程序启动时调用 register 方法:
public class MvcApplication : HttpApplication
{
protected override void OnApplicationStarted()
{
// ...
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// ...
}
}
Wouter de Kort在他的回答中已经解释了这背后的概念。