如何以及在何处处理 3 层 Web 应用程序中的异常?特别是 Sql 数据库异常
Posted
技术标签:
【中文标题】如何以及在何处处理 3 层 Web 应用程序中的异常?特别是 Sql 数据库异常【英文标题】:How and where to handle exceptions in a 3-tier web application? Specifically Sql Database Exceptions 【发布时间】:2011-01-09 13:29:40 【问题描述】:我正在构建标准的 3 层 ASP.NET Web 应用程序,但我正在为在哪里做某些事情而苦恼——特别是处理异常。
我试图在网上浏览一些示例,但找不到任何可以显示整个项目如何链接在一起的示例。
在我的数据层中,我连接到 SQL Server 并做一些事情。我知道我需要捕获可能因此引发的异常,但我不确定在哪里做。
根据我的阅读,我应该在 UI 层执行此操作,但在这种情况下,我不确定如何确保与数据库的连接已关闭。有没有人能够澄清如何做到这一点?另外,如果有人知道我在哪里可以找到遵循最佳实践的示例 3 层 Web 应用程序,那也很棒。
谢谢
【问题讨论】:
【参考方案1】:这不是您问题的具体答案,但如果您对最佳实践感兴趣,我会查看 Microsoft 模式和实践:
Application Architecture Guide
Web Applications Guides
【讨论】:
【参考方案2】:没有简单的答案或模式可以确保成功。就像您的验证策略一样,您的确切异常处理策略是针对您的具体情况的,并且通常是时间和全面性之间的权衡。不过,我们可以提供一些好的建议:
永远不要隐藏堆栈跟踪;除非出于安全目的您想隐藏发生的事情,否则永远不要使用“重新抛出”。 不要觉得到处都需要错误处理。默认情况下,在您的较低层中,让实际错误渗透到顶层也不错。用户界面/控制器是您真正决定如何应对出现问题的地方。 在每一点上,如果出现问题,作为您自己,您究竟想要发生什么。通常,您将无法想出比让它上升到顶层甚至客户端计算机更好的方法。 (尽管在详细报告的生产阶段。)如果是这种情况,就让它过去吧。 确保处置非托管资源(任何实现 IDisposable 的资源。)您的数据访问就是一个很好的例子。在 Finally 块中为您的(尤其是)连接、命令、数据读取器等调用 (A) .Dispose(),或者 (B) 使用 Using Syntax/Pattern 确保正确处置发生。 寻找可能出现错误的地方,以及可以在哪里寻找某些错误、做出反应(通过重试、等待第二次重试、以不同的方式尝试该操作等),然后希望成功。您的大部分异常处理是为了让成功发生,而不仅仅是为了很好地报告失败。 在数据层,您必须考虑如果在多步骤过程中出现问题该怎么办。您可以让实际的错误渗透出来,但是这一层必须在错误发生后处理整理。您有时会想要使用事务。 在异步情况下((A.)因为多线程或(B.)因为业务逻辑是在“任务机器”上单独处理,等等),你特别需要有一个计划记录错误。 我宁愿在 25% 的应用程序中看到“错误处理代码”,而不是 100%。 100% 意味着您可能希望它看起来和感觉就像您有错误处理。 25% 表示您花时间处理真正需要处理的异常。【讨论】:
感谢您的回复。你对处理 SQLExceptions 有什么想法?对于 sproc 中引发的错误,您会在 DAL 中捕获这些错误并在此处抛出自定义错误或其他替代方法吗? 在我当前的 DAL(更多的是一个 DA 工具,我将它传递到我的业务对象中,这些对象也是一种半 DAL 对象)我捕获并处理了一些特定的自动错误(如死锁受害者错误#1205。如果我看到了,我会再试一次。),但让大多数错误渗透。有时,业务对象可以针对该错误做一些智能的事情;如果不是,我让它渗透。在我的 DAL 中,我很快将自动记录错误以及 sql 命令文本和参数。我考虑过将 sql 文本添加到错误消息中,但这是一个安全问题。 只有在可以 (A) 恢复/反应/可能成功,或者如果 (B) 可以的情况下,才在 sprocs/udf 中捕获错误改进渗出的错误信息。因此默认情况下在 sproc/udf 中没有错误处理,但是您有时会向它们添加错误处理。 再次感谢您的帮助。我正在考虑在我的存储过程中引发用户定义的错误,然后我将在 DAL 层中捕获这些错误,然后为它们创建并抛出一个自定义异常,而不仅仅是标准的 sqlexception。也许我应该在我的存储过程中使用返回码来确定它是否成功。 从你所说的来看,我猜这是要走的路。您不想将异常处理的陷阱用于逻辑流。无论您是检查特定条件,还是假设特定条件并引发错误,都是一个判断调用。【参考方案3】:我认为最好的做法是在最后负责的时刻处理异常。这通常意味着在 UI 级别(即 MVC 应用程序中的控制器或传统 asp.net 应用程序的代码隐藏中)。在这个高层次上,您的代码“知道”用户在问什么,以及如果某些事情不起作用需要做什么。
在调用堆栈的较低位置处理异常通常会导致调用代码无法正确处理异常情况。
在您的数据层中,您将使用标准模式(例如,using
statement 用于 IDisposables,例如 SqlConnection),可能会发生您知道的文档异常(不要这样做内存或其他罕见情况),然后让它们在调用堆栈中向上流动。在调用者可能必须处理许多异常的情况下,您最多可能希望捕获这些异常并将它们包装在一个异常类型中。
如果您需要在放开异常之前“清理”一些东西,您可以随时使用finally
block 进行清理。您不必catch
即可使用finally
块。
我不知道任何旨在突出正确异常处理的小型网站示例,抱歉。
【讨论】:
那么使用using语句等待垃圾回收关闭连接就可以了吗? 不,您想在完成后立即关闭连接 (a),或 (b) 在 finally 块中,或使用使用语法/模式,这样会自动发生。我会详细说明。 . . @nick 检查using
声明的答案中的链接。
干杯 Will,我之前确实看过,但错过了它说即使引发异常也会处理对象的位 - 漫长的一天。看起来这是一个必须具备的模式,我将从现在开始使用它。【参考方案4】:
有一点可能会引导您思考:如果您有任何类型的卷可能导致并发问题(死锁),您将希望您的应用程序检测到特定的 SQL 错误并重试操作(例如事务)。这就要求在数据层或业务层进行一些异常处理。
-克里普
【讨论】:
为克里普的评论喝彩,在这些情况下会记住这一点以上是关于如何以及在何处处理 3 层 Web 应用程序中的异常?特别是 Sql 数据库异常的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring 和 Hibernate 为 Web 应用程序和批处理作业设置事务