我想知道人们对在存储过程中使用 RAISERROR 将用户消息(即与业务相关的消息,而不是错误消息)传回应用程序的想法。
我公司的一些高级开发人员一直在使用这种方法,并在我们的 C# 代码中捕获 SqlException 来获取消息并将它们显示给用户。我对这种方法不满意,想知道其他人如何处理来自存储过程的这些类型的用户消息。
我想知道人们对在存储过程中使用 RAISERROR 将用户消息(即与业务相关的消息,而不是错误消息)传回应用程序的想法。
我公司的一些高级开发人员一直在使用这种方法,并在我们的 C# 代码中捕获 SqlException 来获取消息并将它们显示给用户。我对这种方法不满意,想知道其他人如何处理来自存储过程的这些类型的用户消息。
我已经这样做了,但它通常是为了传递业务“错误”消息,本质上是一个数据配置必须到位,无论出于何种原因,标准 FK 约束都无法强制执行。
如果它们实际上是“错误”,我对此没有太大问题。如果它正在插入一条记录并使用 RAISERROR 抛出(“您已成功注册 XYZ!”),那么您就有问题了。如果是这样的话,我可能会想出一个使用 out 参数的团队/部门/公司开发标准。
像这样使用 RAISERROR 真的不是一个好主意。这就像使用异常作为流控制逻辑一样,这通常是不受欢迎的。
为什么不使用 OUT 参数呢?这正是他们的目的。我想不出不支持 OUT 参数的数据库或客户端 API。
使您的存储过程返回 2 组数据。第一个可以包含实际返回的数据,然后第二个可以返回文本消息。然后,您的应用程序代码可以在需要的地方使用数据,然后显示返回的任何消息。
回答这么老的问题是不是很糟糕?随便...
对于您的日常状态消息,这将是一件坏事,我几乎同意上面的每个答案。然而,我已经看到这非常有效地用于显示长批次的进度。有关示例,请参阅Jens K从批处理和存储过程中获取反馈/进度。你必须有一个非常核心的理由去做它,但是当你需要它时,你需要它并且它很棒。
仅当情况异常并且您没有处理逻辑时才应抛出异常。引发和捕获异常也很昂贵。
避免使用异常,除非
使用输出参数或返回多个结果集来传递来自存储过程的信息。
我会尽量避免让我的存储过程返回业务相关消息,因为根据定义,这些消息可能应该在业务逻辑层中处理/生成。
例外应该是例外的(不经常)。我只会将 RAISEERROR 用于错误(例如,嘿,我试图导入所有这些数据,其中一行有愚蠢的数据,所以我回滚了事务)。您还需要非常小心所引发错误的严重性,这可能会对错误的传播方式以及连接发生的情况产生巨大影响。
如果这还不够,请尝试使用返回值或输出变量。
我想如果您不介意检查列,那么您可以根据发生的情况返回不同的内容。
如果一切正常,则正常返回数据。如果有问题,请返回一个结果,其中包含一个名为 Error 的列,该列描述了什么是错误的。在处理数据之前检查该列的列名并采取相应措施。
如果你真的反对 RAISERROR,那我就别想了。
我使用 raiseerror 从多个嵌套存储过程的深处返回,但是存储过程的最后一层总是在被引发到调用语言(在我们的例子中是通过 JDBC 的 Java)之前捕获异常。最外层的存储过程 catch 被转换为要传输到 JDBC 调用的 XML 消息,按照我们的约定,根元素必须包含一个反馈属性。反馈属性的值始终具有 ok、alert 或 error 的装饰器。好的意思是继续,这里没什么可看的。警报意味着继续,但向用户显示其余的反馈。错误意味着平底船,请致电服务台。
我会使用存储过程中的 RETURN 值,如下所示:
CREATE PROCEDURE checkReturnValue
AS
BEGIN
DECLARE @err AS INT
SET @err = 0
IF (rand() < 0.5)
BEGIN
SET @err = 1
END
SELECT * FROM table
PRINT @err
RETURN @err
END
检查调用存储过程的应用程序中的 RETURN 值。
仅当“业务”错误消息是数据库错误消息时,为了满足数据库级别的基本低级业务需求而设置的数据库约束正在被违反。
它不应该用于高级业务逻辑,在数据库层应该避免使用它。数据库层总是最慢的变化,所以只有非常缓慢变化和不变的业务逻辑应该在那里。
因此,对于非活动/禁用客户的订单消息可能是肯定的,但对于在 90 天内有余额的客户的订单则不是。第一条规则可能是永久性的,第二条规则可能是可配置的,取决于企业每月的突发奇想。
我们在发生错误时引发错误,并在输出变量或返回值中返回状态信息。
您应该使用 SQL 存储过程输出参数。
来自http://msdn.microsoft.com/en-us/library/ms378108.aspx:
CREATE PROCEDURE GetImmediateManager
@employeeID INT,
@msg varchar(50) OUTPUT
AS
BEGIN
SELECT ManagerID
FROM HumanResources.Employee
WHERE EmployeeID = @employeeID
SELECT @msg = 'here is my message'
END
如果不能更早地检查/捕获它,则可能很难做其他任何事情。
我不得不在我编写并用作插入/更新表的约束的过程中编写 raiseerror,因为它是数据的“最后一站”,我必须在那里检查它。
我认为一般来说,如果你从数据库中得到错误。它很痛苦,而且很难不费力气地向用户提供“好的”反馈,但有时你只是不知道,直到你插入/更新:P
我亵渎神明的 2 美分:
基于文本的消息在 Web 上运行得非常好,例如 HTTP。创建、发送、调试系统很容易,其中消息以人类可读的文本完成。
所以,你的 SQL Server 层和它上面的层之间的消息传递可能是同样的事情。使用文本作为消息传递的一部分可能会使您的开发更加敏捷,调试更加容易。也许高级开发人员是务实的,您也许应该对先入为主的正确性概念持开放态度。
辩论设计选择本身的优点,而不是基于正确性的概念。(软件开发也有时尚)