C# try-catch块中的代码重复

C# try-catch块中的代码重复,c#,asp.net-mvc-4,exception-handling,C#,Asp.net Mvc 4,Exception Handling,是否有更好的方法捕获异常?我好像在复制很多代码。基本上,在每个控制器中,我都有一个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

是否有更好的方法捕获异常?我好像在复制很多代码。基本上,在每个控制器中,我都有一个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();
}

也可以使用单例模式:

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) {}
    ...
}

编辑 有些人出于合理的理由不喜欢单身。我们可以使用一些DI代替单身:

class Controller
{
    private ILogger logger;

    public Controller(ILogger logger)
    {
        this.logger = logger;
    }
}

并使用一些DI库将ILogger的一个实例注入控制器。

您不需要在每个方法上都放置try/catch块。那是乏味和痛苦的!相反,您可以使用Global.asax的Application\u 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应用程序的三大静默性能杀手”正如我在本博客中所解释的那样,因为我非常不喜欢
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)
}

您试图做的事情被称为交叉关注点。您正在尝试记录代码中任何地方发生的任何错误

在ASP.NET中,可以通过使用过滤器来实现MVC横切关注点。过滤器是可以全局应用于控制器或方法的属性。它们在动作方法执行之前或之后运行

您有几种类型的过滤器:

  • 授权筛选器,它们运行以检查是否允许用户访问资源
  • 动作过滤器,它们在动作方法执行之前和之后运行
  • 结果过滤器,这些过滤器可用于更改操作方法的结果(例如,向输出中添加一些额外的HTMl)
  • 每当抛出异常时,都会运行异常筛选器
在您的例子中,您正在寻找异常过滤器。这些过滤器仅在操作方法中发生异常时运行。您可以全局应用过滤器,以便它将自动为任何控制器中的所有异常运行。您还可以在某些控制器或方法上专门使用它


您可以找到如何实现自己的过滤器。

我喜欢建议通用解决方案的答案,但是我想指出另一个适用于MVC的解决方案。 如果您有一个通用的控制器库(无论如何,这是IMO的最佳实践)。您可以简单地重写OneException方法:

public class MyControllerBase : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        DoSomeSmartStuffWithException(filterContext.Exception);
        base.OnException(filterContext);
    }
}
然后简单地从公共基继承普通控制器,而不是从控制器继承普通控制器

public class MyNormalController : MyControllerBase 
{
    ...

如果您喜欢这个,您可以查看控制器类中其他方便的虚拟方法,它有很多。

我在应用程序中使用了一个特殊的类,名为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在他的回答中正确地指出的那样,这是一个跨领域的问题,因此我将该类放在我的应用程序层中,并将其作为服务使用。如果将类定义为接口,则可以在不同的场景中使用不同的实现

在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已经解释了这个概念。

查看“ASP.NET MVC4中的面向方面编程”@ AHMMEKRAIEM,或者只需降到C++,并使用宏来表示一切。AOP是一种黑客-如果我可以的话。@Gusdor“AOP是个黑客。”这是你的意见。但尽管如此,AOP并不是这里的最佳解决方案,@gusdor Ahmed提到的定制操作过滤器不需要IL屠宰。我知道AOP在.NET中还没有得到很好的支持,但属性是一种实现横切关注点的方法,而无需手动向每个关注点添加代码method@Gusdor-我明白了。你应该学会更隐晦地掩饰你的讽刺:请不要使用单身汉。SUNITLON是一种反模式,它使测试变得困难、困难、困难,并且应该避免。@ DavidArno,我在C++代码中使用“SuntLon”类。您对“Factory”类或某些管理器类(例如“ThreadManager”或“ThreadPool”)而不是Singleton有何建议?@Mohammadderb Deendency injection,如您所述。由于未知的副作用,单例使您的代码更难阅读(代码在您未传入的某些全局可用对象上执行方法)。DI清楚地说明了你的代码依赖于什么。@DavidArno:小心刻板印象!静态扩展方法(您投票赞成)与单例中包含的日志记录方法一样难以测试。@DavidArno:我恐怕不明白您的意思。将
publicstaticsomemethoda(){…}
publicstaticmyclass实例=newmyclass()进行比较;私有MyClass(){}公共SomeMethodB(){…}
SomeMethodA
SomeMethodB
都同样难以测试!唯一的区别是第一个是使用
MyClass.SomeMethodA()
执行的,第二个是使用
MyClass.Instance.SomeMethodB()
执行的。如此简单,如此聪明,
public class MvcApplication : HttpApplication
{
    protected override void OnApplicationStarted()
    {
       // ...
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       // ...
    }
}