尝试捕获块中的代码重复

本文关键字:代码 | 更新日期: 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在他的回答中已经解释了这背后的概念。