error handling我应该抛出exception吗? 或者在源头处理?

我有这种格式

asp.net MVC视图 – >服务层 – >存储库。

因此视图调用服务层,其中包含业务/validation逻辑,然后调用存储库。

现在,我的服务层方法通常具有bool返回类型,因此如果数据库查询已经完成,我可以返回true。 或者如果失败了。 然后向用户显示通用消息。

我当然会用elmah记录错误。 但是我不确定我应该如何达到这一点。

就像现在我的Repository有更新,创建,删除的void返回类型。

所以说如果更新失败,我应该在我的存储库中有一个try / catch引发错误,然后我的服务层捕获它并执行elmah信令并返回false?

或者我应该让这些存储库方法返回“bool”,尝试/捕获存储库中的错误,然后将“true”或“false”返回到服务层,然后返回“true”或“false”到视图?

exception处理仍然让我感到困惑的是如何处理错误以及何时抛出以及何时捕获错误。

我经常使用的经验法则是:

  • 在低水平时,由于特殊情况而无法完成操作时抛出。
  • 在中间层中,捕获多个exception类型并重新包装在一个exception类型中。
  • 在最后一个负责任的时刻处理exception。
  • 文献!

以下是多层ASP.NET MVC应用程序(UI,Controller,Logic,Security,Repository)的伪代码示例:

  1. 用户点击提交按钮。
  2. 执行控制器操作并调用逻辑(业务)层。
  3. 逻辑方法使用当前用户凭据调用Security
    • 用户无效
      • 安全层抛出SecurityException
      • 逻辑层捕获,在LogicException中包含更通用的错误消息
      • Controller捕获LogicException,重定向到Error页面。
    • 用户有效且安全性返回
  4. 逻辑层调用存储库以完成操作
    • 存储库失败
      • 存储库抛出RepositoryException
      • 逻辑层捕获,在LogicException中包含更通用的错误消息
      • Controller捕获LogicException,重定向到Error页面。
    • 存储库成功
  5. 逻辑层返回
  6. Controller重定向到Success视图。

注意,Logic层只抛出一个exception类型 – LogicException。 冒泡的任何低级exception都会被捕获,包含在抛出的LogicException的新实例中。 这给了我们很多好处。

首先,可以访问堆栈跟踪。 其次,调用者只需要处理单个exception类型而不是多个exception。 第三,可以按摩技术exception消息以显示给用户,同时仍保留原始exception消息。 最后,只有负责处理用户输入的代码才能真正了解用户的意图,并确定操作失败时的适当响应。 存储库不知道UI是否应显示错误页面或请求用户使用不同的值再次尝试。 控制器知道这一点。


顺便说一句,没有什么说你不能这样做:

try { var result = DoSomethingOhMyWhatIsTheReturnType(); } catch(LogicException e) { if(e.InnerException is SqlException) { // handle sql exceptions }else if(e.InnerException is InvalidCastException) { // handle cast exceptions } // blah blah blah } 

虽然返回错误(或成功)代码通常是更好的方法,但exception比返回代码或静默抑制错误有一个巨大的优势:至少你不能忽略它们!

不要滥用简单流量控制的例外 – 这将是最愚蠢的事情。

但是如果你的某个function真的遇到了“特殊”的问题,那么肯定会抛出一个执行。 然后调用者必须明确地处理它,从而知道发生了什么,或者它会轰炸他。

只是返回一个错误代码是危险的,因为调用者可能只是不打扰检查代码并且可能仍然继续 – 即使在你的应用程序的逻辑中,确实有问题需要处理。

所以:不要滥用exception,但如果发生真正的exception需要调用者对其做些什么,我肯定会建议使用该机制来发出exception情况。

至于处理exception:处理那些你真正可以处理的exception。 例如,如果您尝试保存文件并获得安全exception,请向用户显示一个对话框,询问要保存的其他位置(因为他可能没有权限保存到他想要的位置)。

但是,您无法真正处理的exception(您想要对“OutOfMemoryexception”做什么,真的?)应该保持不变 – 也许呼叫堆栈中的调用者可以处理那些 – 或者不是。 渣

我喜欢以这种方式考虑exception处理:您可以定义方法签名,以及您希望做什么。 现在,如果你无法做到这一点,那么你必须抛出exception。 因此,如果您希望根据您拥有的输入数据(忽略环境状态)而失败,那么您的方法签名必须指示操作是成功还是失败。 但是,如果您的方法不希望基于您的输入(再次忽略所有其他环境状态)而失败,那么当方法失败时,exception就会顺序排列。

考虑这两个API:

 int int.Parse(string integerValue); // In this case, the method will return int // or it will die! That means your data must be // valid for this method to function. bool int.TryParse(string integerValue, out number); // In this case, we expect the data // we passed in might not be fully // valid, hence a boolean. 

首先,没有一种方法,肯定没有一种完美的方式,所以不要过度思考它。

通常,您希望对exception情况使用exception(exception会导致性能开销,因此过度使用它们尤其是在“循环”情况下会产生性能影响)。 因此,假设存储库由于某种原因无法连接到数据库服务器。 然后你会使用一个例外。 但是,如果存储库通过id执行某个对象的搜索并且找不到该对象,那么您将希望返回null而不是抛出exception,表示ID为x的对象不存在。

validation逻辑也是如此。 由于它正在validation,因此假设有时输入不会validation,因此在这种情况下,最好从validation服务返回false(或者可能是更复杂的类型,包括一些关于它为什么不validation的附加信息)。 但是,如果validation逻辑包括检查是否使用了用户名,并且由于某种原因它无法执行此操作,那么您将抛出exception。

所以说如果更新失败,我应该在我的存储库中有一个try / catch引发错误,然后我的服务层捕获它并执行elmah信令并返回false?

为什么更新会失败? 有没有一个完美的理由发生这种正常过程的一部分? 然后如果由于一个奇怪的原因发生exception(假设某些东西在更新之前删除了更新的记录),则不抛出exception然后exception接缝逻辑。 真的没有办法从这种情况中恢复过来。