3

哪个是处理异常的最佳位置?BLL、DAL 还是 PL?

我是否应该允许 DAL 和 BLL 中的方法将异常抛出链上并让 PL 处理它们?还是我应该在 BLL 处理它们?

例如

如果我的 DAL 中有一个方法发出“ExecuteNonQuery”并更新一些记录,并且由于一个或多个原因,0 行受到影响。现在,我应该如何让我的 PL 知道是否发生了异常或确实没有与条件匹配的行。我应该在我的 PL 代码中使用“try catch”并通过异常让它知道,还是应该在 DAL 处处理异常并返回一些特殊代码,如 (-1) 以让 PL 区分(异常)和(否行匹配条件,即零行受影响)?

4

7 回答 7

5

让 DAL 中引发的异常上升到 PL 是没有意义的——如果无法建立数据库连接,用户应该如何反应?

如果可以处理异常,请尽早捕获和处理异常。不要在没有输出提示或日志消息的情况下吞下它们——这将导致严重的困难和难以跟踪的错误。

于 2010-06-14T18:47:28.317 回答
3

简短的回答是视情况而定!

只有当你能用它做一些有用的事情时,你才应该处理一个异常。“有用的东西”再次取决于你在做什么。您可能想要记录异常的详细信息,尽管这并没有真正处理它,并且在大多数情况下您应该在登录后重新抛出异常。您可能希望将异常包装在其他一些(可能是自定义的)异常中,以便向异常添加更多信息。正如@mbeckish 所涉及的那样,您可能希望通过重试操作来尝试从异常中恢复 - 但是您应该小心不要永远重试。最后(请原谅双关语)您可能希望使用 finally 块来清理任何资源,例如打开的数据库连接。您选择对异常执行的操作将影响您处理它的位置。很可能没有'

当你自己抛出异常时,你应该只在“异常”情况下抛出异常,因为抛出异常会有很大的开销。在您的示例中,您建议如果您的操作没有更新任何记录,您可能会考虑抛出异常。这真的很特殊吗?在这种情况下,更好的做法是返回更新的记录数 - 这可能仍然是需要向用户报告的错误情况,但并不像命令失败那样异常,因为与数据库的连接已下降;消沉。

是一篇关于异常处理最佳实践的合理文章。

于 2010-06-14T17:43:06.090 回答
3

这是一个巨大的话题,有很多不必要的争议(声音很大的人会提供不好的信息!)如果你愿意处理这个问题,请听从 s1mm0t 的建议,这基本上是可以接受的。

但是,如果您想要一个单词的答案,请将它们放在 PL 中。严肃的。如果您可以摆脱它,请将您的错误处理放在全局异常处理程序中(出于安全原因,所有错误都应记录并提供代码以在生产中查找日志(尤其是 Web 时),但在开发过程中提供完整的详细信息速度原因)。

编辑:(澄清) 你必须在任何地方处理一些错误——但这不是“每个功能”的规范。大多数时候,让他们冒泡到 PL 并使用您自己的代码处理 .NET 的全局错误:通过一个通用例程从那里记录完整的调用堆栈,该例程可通过事件处理程序从所有 3 层访问(请参阅消息底部的编辑)。这意味着您不会在所有代码中都使用 try/catch;只是您期望和错误的部分,并且可以在那里处理它,或者,非关键部分,您使用它们记录错误并通知用户不可用的功能(这更罕见,对于超级可靠/关键程序)

除此之外,在处理资源有限的项目时,我经常使用 'using' 关键字或 try/finally/end try 而没有捕获。用于多线程锁/互斥/重新进入预防标志/等。您还需要在所有情况下都尝试/最终,这样您的程序仍然可以工作(尤其是有状态的应用程序)。

如果您不正确地使用异常(例如,当您应该使用 IF 语句或检查它时处理非错误,那么在您尝试之前,一个不确定的操作将起作用),这种理念将更加崩溃。

附带说明,在胖客户端应用程序中,尤其是在可能丢失大量数据或用户输入的情况下,在尝试保存数据的地方使用更多尝试/捕获可能会更好(当然标记为尚未生效) )。

编辑:另一个需要至少在 PL 中有记录例程- 这将根据平台而有所不同。我们正在开发的一个应用程序与 3 个 PL 版本共享 BLL/DAL:一个 ASP.Net 版本、一个 winforms 版本和一个控制台应用程序批处理模式回归测试版本。调用的日志记录例程实际上在 BLL 中(DAL 只抛出错误或完全处理它获得或重新抛出的任何错误)。然而,这引发了一个由 PL 处理的事件;在 Web 上,它将它放在服务器的日志中,并进行 Web 样式的错误消息显示(对生产友好的消息);在 WinForms 中会出现一个带有技术支持信息等的特殊消息窗口,并在后台记录错误(开发人员可以做一些“秘密”来查看完整信息)。当然,在测试版本中,这是一个更简单的过程,但也有所不同。
不知道除了传递参数“什么平台”之外,我会如何在 BLL 中做到这一点,但由于它不包括日志记录所依赖的 winforms 或 asp 库,所以这仍然是一个技巧。

于 2010-06-14T18:13:58.667 回答
1

知道如何设置正确的层应该是处理异常的层。例如,如果您决定通过重试查询一定次数来处理死锁错误,那么您可以将其构建到您的 DAL 中。如果它继续失败,那么您可能希望让异常冒泡到下一层,然后它可以决定它是否知道如何正确处理此异常。

于 2010-06-14T17:27:25.643 回答
0

应用程序中的所有层都应该优雅地管理异常。这被称为横切角,因为它出现在您的所有层中。我相信使用像 Enterprise Exception Block 这样的统一框架,你最终会得到一个更好的代码。看看这个帖子

http://msdn.microsoft.com/en-us/library/ff664698(v=PandP.50).aspx

掌握它需要一些时间,但是那里有很多示例和截屏视频。

于 2010-06-14T18:42:20.193 回答
0

如何处理异常取决于技术和业务需求。对于复杂或非常重要的数据库更新,我包括将一小部分已知错误备份传递给 DL 的参数。这样,在某些情况下,可以通过编程方式解决已知的错误场景。在其他情况下,需要记录错误并通知用户错误。

我习惯于将错误通知人类。当然,日志记录会给我们提供详细的信息,但它不能替代人类的响应时间。不仅如此,为什么还要强迫开发人员查看系统日志只是为了看看事情是否进展顺利?谈论不必要的成本。

如果您有时间定义潜在的错误/异常并以编程方式解决它们,那么一定要这样做。很多时候错误/异常是意料之外的。这就是为什么为意外做好准备很重要,还有什么比让人类参与更好的方法来做到这一点。

总体而言,在计划异常处理时应该采取防御措施。程序增长或死亡。增长的一部分是引入错误。所以不要旋转你的轮子试图杀死他们。

于 2010-06-15T16:34:33.040 回答
-2

你的问题是异常在哪里相关?如果它是一个数据访问异常,它应该在 DAL 中被捕获。如果是逻辑异常,则应在 BLL 中捕获。如果它是一个表示异常,那么在 PL 中。

例如,如果您的 DAL 抛出异常,它应该返回 null 或 false 或者您的 BLL 的任何情况。你的 BLL 应该知道如果 DAL 返回一个空值该怎么办,也许它直接通过它,也许它尝试调用另一个函数等。如果 BLL 从 DAL 传递一个空值或返回特定的东西,你的 PL 也是如此那么表示层应该能够通知最终用户存在问题。

当然,您不会收到冗长的异常消息,但就您的用户而言,这是一件好事。您应该有一个灵活的日志系统来捕获这些异常并将它们报告给数据库或 ip:port 或您决定的任何东西。

本质上,如果关注点是数据问题或逻辑问题,您需要考虑分离关注点,它应该相应地处理。

于 2010-06-14T17:42:09.227 回答