2017-05-24 91 views
1

在.net中,当我使用SqlCommand并调用一个使用RAISERROR抛出的存储过程时,我可以读取由该过程设置的输出变量。但是,从T-SQL批处理中调用该过程时,OUTPUT参数看起来完全没有设置。如何在调用RAISERROR之前读取由存储过程设置的批处理中的OUTPUT参数?

当过程调用RAISERROR来指示错误时,如何从调用T-SQL中读取OUTPUT参数@OUT_x

CREATE PROCEDURE dbo.p_x (
    @OUT_x INT OUTPUT 
) AS 
BEGIN 
    SET @OUT_x = 1; 
    SELECT 'inner ', @OUT_x; 
    RAISERROR('yo', 11, 1); 
END 
GO 

DECLARE @x INT = 0; 
SELECT 'initial', @x; 
BEGIN TRY 
    EXEC dbo.p_x @x OUTPUT; 
END TRY 
BEGIN CATCH 
    SELECT 'catch', @x; 
END CATCH 
SELECT 'end', @x; 

输出:

------- ----------- 
initial 0 


------ ----------- 
inner 1 


----- ----------- 
catch 0 


---- ----------- 
end 0 

我证明@OUT_x被示出经由RAISERROR抛之前其在存储过程中的值设置为1。我预计“catch”和“end”条目是1,但它好像OUTPUT参数值复制只发生在成功的存储过程完成时。 I know I can read these OUTPUT parameters when I call stored procedures through SqlCommand - 为什么T-SQL批处理必须有任何不同?

是否有解决方法,不涉及更改存储过程?我真的希望我的存储过程在异常情况下抛出(RAISERROR),但我还需要使用OUTPUT参数来传递错误详细信息。

+0

T-SQL批处理没有区别。如果你把这整段代码放到'SqlCommand'中,你会得到相同的结果。你的问题实际上是为什么'TRY .. CATCH'正在做它正在做的事情(当存储过程执行'RAISERROR'时不复制输出参数)。厄兰德[涵盖此](http:// sommarskog。se/error_handling/Part2.html#systemfunctions)在他的无与伦比的错误处理文章,但基本上,这只是它的工作方式。你不能使用'TRY .. CATCH'并且得到错误之前分配的输出参数的值。 –

+1

@JeroenMostert'SqlCommand'返回'1'的参数就像你期望的那样。 – GSerg

+1

@binki,@GSerg:即使你可以(并且我不知道你是否可以,我还没有测试过),这里的关键问题是'try {...} catch {...}'in托管代码与T-SQL中的TRY ... CATCH不同。我正在谈论*执行整个代码块*,包括'TRY ... CATCH',作为'SqlCommand'。你真的告诉我结果不一样吗?很显然,如果可以的话,在T-SQL之外处理错误*因为在T-SQL内部处理它们是一件很混乱的事情。但我从这个问题中收集到的,这正是这里没有的选项。 –

回答

4

这是如何TRY .. CATCH作品的限制:当发生RAISERROR并进入CATCH块,它不输出参数的值复制回相应的局部变量。有这样说(结果可能不会被依赖,因为毕竟发生了错误),但它可能是一个疏忽。对于初学者来说,不要试图忽略存储过程产生的任何结果集(并且把结果放在那里有一种可能的解决方法,但它需要改变存储过程)。据我所知,这种行为没有正式记录,因为很多T-SQL错误处理的东西都没有,但是Erland Sommarskog的excellent article提到了它。

由于结果回传的方式,客户端代码不会遇到同样的问题:当分配输出参数时,发回TDS数据包以指示;当发生错误时,会发送另一个TDS数据包来指示该错误。这些事情按时间顺序发生。一个客户端将不得不积极丢弃上的输出参数上的一个错误,而不仅仅是忘记将它们复制为TRY .. CATCH

如果所有您使用的输出参数是通信错误的详细信息,使用RAISERROR但让你的存储过程返回客户端处理错误代码考虑。当然,你会冒着外部客户忘记检查这个的风险,因为他们实际上不会得到SqlException。另一种选择是将您要传递到的邮件中的所有详细信息编码为RAISERROR,或者在ERROR_STATE()中编码,如果它是小整数。这仍然需要修改存储过程;只要你打算使用TRY .. CATCH就没有办法。

+1

正如你所建议的那样,这个功能可能是一个疏忽,但是自2012年的THROW以同样的方式工作,这看起来更像是一个有意识的设计决定。或者,也许它最初是一个疏忽,他们决定离开;) –

+0

谢谢。这清楚地表明我在寻求不可能的事情,并为替代方法提供了很好的建议。 – binki

+0

尽管结果集通过了,但是从其他T-SQL使用存储过程的结果集不是不可能的吗? – binki

相关问题