故障或不故障

时间:2010-12-14 15:45:57

标签: wcf exception architecture faults

我正在与同事讨论何时抛出错误以及何时不在WCF服务中抛出错误。

有一种观点认为,当服务操作由于某些错误而无法完成工作时,我们只会抛出错误;由于它,某些东西可能处于无效状态。所以,举一些例子:

  • ValidateMember(字符串名称,字符串密码,字符串国家/地区) - >如果未传递强制参数,则会抛出错误,因为验证本身无法执行; - >如果发生了一些内部错误,就像数据库已关闭一样会抛出错误 - >将在所有其他情况下返回状态合同,指定验证结果(MemberValidated,WrongPassword,MemberNotKnown,...)

  • GetMember(int memberId) - >只有在某些事情发生故障时才会抛出错误,在所有其他情况下它会返回成员,如果没有找到则返回null

另一种意见是,当GetMember找不到该成员时,我们也应该抛出错误,或者在ValidateMember的情况下,密码错误。

你怎么看?

5 个答案:

答案 0 :(得分:12)

我对此的看法......

失败有三个原因:

  1. 服务代码抛出异常,例如数据库错误,代码中的逻辑错误。这是你的错。
  2. 客户端代码未能根据您的文档正确使用您的服务,例如:它没有设置必需的标志值,它无法传入ID。这是客户端软件开发人员的错。
  3. 最终用户在屏幕上键入一些愚蠢的内容,例如缺少出生日期,负薪水。这是最终用户的错。
  4. 由您决定如何将实际故障合同映射到每个故障原因。例如,我们这样做:

    • 对于原因1和2,所有客户端代码需要知道的是服务失败。我们定义了一个非常简单的“致命错误”错误合同,它只包含一个唯一的错误ID。该错误的完整详细信息记录在服务器上。
    • 对于原因3,最终用户需要确切地知道他/她做错了什么。我们定义了一个“验证错误”错误合同,其中包含一系列友好错误消息,供客户端代码显示在屏幕上。

    我们借用原因3的Microsoft EntLib class,并使用exception shielding以声明方式处理原因1和2。它使代码非常简单。

    澄清:

    我们在服务中处理这样的三个原因:

    1. 服务代码中抛出了意外的异常。我们在顶层捕获它(实际上异常屏蔽捕获它,但原理是相同的)。记录完整的详细信息,然后将FaultException<ServiceFault>抛给仅包含错误ID的客户端。
    2. 我们验证输入数据并故意抛出异常。它通常是ArgumentException,但任何合适的类型都可以。一旦抛出它,它的处理方式与(1)完全相同,因为我们希望它与客户端看起来相同。
    3. 我们验证输入数据并故意抛出异常。这一次,它是FaultException<ValidationFault>。我们将异常屏蔽配置为通过un-wrapped传递此异常屏蔽,因此它在客户端上显示为FaultException<ValidationFault>而不是FaultException<ServiceFault>
    4. 最终结果:

      • 服务中没有任何catch块(干净利落的代码)。
      • 如果客户希望向用户显示消息,则只需捕获FaultException<ValidationFault>。所有其他异常类型(包括FaultException<ServiceFault>)都由客户端的全局错误处理程序处理为致命错误,因为服务中的致命错误通常也意味着客户端中的致命错误。

答案 1 :(得分:3)

这是一种常见的常规故障,然后抛出故障是一个错误。应该编写软件来处理例行项目,例如输入错误的密码。故障处理是出现异常故障,不属于程序正常设计的一部分。

例如,如果您的程序编写时认为它始终可以访问数据库,并且数据库无法访问,那么“修复”远远超出了软件的限制。应该抛出一个错误。

故障处理在编程语言的结构中使用不同的逻辑流程,只有当你“离开”编程问题的正常处理时才使用它,你将使你的解决方案利用编程语言的特性。一种看起来更自然的方式。

答案 2 :(得分:2)

我认为将错误处理和错误处理分开是一种好习惯。任何错误情况都应由您的程序处理 - 故障处理保留用于特殊情况。作为两者分离的指南,我发现在考虑这些情况时要记住只有三种类型的错误(处理数据和消息时)和只有一种类型的错误时,它很有用。 错误类型与不同类型的验证有关:

  1. 消息验证 - 您可以根据消息内容确定数据有效或无效。

    示例:旨在作为出生日期的内容 - 您可以从数据中判断它是否有效。

  2. 上下文验证 - 您只能通过引用消息来确定内容无效 结合系统状态。

    示例:加入公司的有效日期早于出生日期。

  3. 对系统说谎 - 您只能在以后的消息中确定消息是错误的 抛出一个异常。

    示例:存储的有效出生日期和对出生证明的检查表明这是不正确的。纠正对系统的谎言通常需要在系统之外采取行动,例如援引法律或纪律补救措施。

  4. 您的系统必须处理所有类别的错误 - 但在第三种情况下,这可能仅限于发出警报。

    相比之下,

    故障(异常)只有一个原因 - 数据损坏(包括数据截断)。示例:未传递验证参数。

    这里适当的机制是故障或异常处理 - 基本上将问题移交给能够处理它的系统的其他部分(这就是为什么应该存在未处理故障的最终目的地)。

答案 3 :(得分:2)

在过去,我们曾经有过一条规则,即异常只适用于特殊和意外的事情。你不想过多使用它们的原因之一是它们“耗费”了很多计算能力。

但是如果你使用例外,你可以减少代码量,不需要很多if else语句,只需让异常冒泡。

这取决于你的项目。最重要的是,有一个项目标准,每个人都以同样的方式做到这一点。

答案 4 :(得分:1)

我的观点是,无论何时该方法应该做什么都不应该抛出异常/错误。所以验证逻辑不应该引发异常,除非无法进行验证(即出于技术原因),但绝不仅仅因为数据无效(在这种情况下,它将返回验证代码/消息或任何帮助调用者纠正数据)。

现在GetMember案例​​很有意思,因为它完全是关于语义的。方法的名称表明可以通过传递id来检索成员(例如,与TryGetMember方法进行比较)。当然,如果无法找到id,或者数据库没有响应但是传递给此方法的错误id可能是在该调用之前某处出错的迹象,则该方法不应该抛出相同的异常。除非用户可以直接在界面中输入member-id,否则在调用该方法之前应该进行验证。

我听到很多关于性能问题的消息。我只是使用C#和trow / catch 1000异常进行了一个简单的测试。 1K Exeptions花费的时间是23ms。每个例外是23μ。我认为性能不再是这里的第一个参数,除非你计划每秒提高超过2000个异常,在这种情况下你会有5%的性能下降,我可以开始考虑。

我的拙见...