我正在从 CLR 存储过程调用 T-SQL 存储过程。T-SQL 中的THROW
语句在不进入catch
块的情况下停止 CLR 的执行。我可以在我的代码中更改某些内容以使 CLR 过程执行该catch
块吗?
T-SQL:
CREATE TABLE t1 (ID INT PRIMARY KEY clustered);
CREATE TABLE t2 (ID INT CONSTRAINT fk_1 FOREIGN KEY REFERENCES t1(ID))
GO
CREATE PROC dbo.TestThrow
AS
BEGIN
BEGIN TRY
INSERT INTO t2 VALUES (1)
END TRY
BEGIN CATCH
THROW
END CATCH
END
GO
CLR:
public static void TestThrow()
{
String query = "EXEC DBO.TESTTHROW";
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
try
{
using (SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
SqlContext.Pipe.Send("no error");
}
}
}
catch (SqlException e)
{
SqlContext.Pipe.Send("sqlexception");
SqlContext.Pipe.Send(e.Message);
}
catch (Exception e)
{
SqlContext.Pipe.Send("exception");
SqlContext.Pipe.Send(e.Message);
}
}
}
这种行为似乎特定于使用
"Context Connection = true;"
. 我试图通过写出try-catch-finally
结构而不是使用using
宏来解决这个问题,但这没有效果。大约 3 个月前,有关此行为的Microsoft Connect 错误已提交。在那个 Connect 错误中,推测会
THROW
引发无法通过Thread.ResetAbort方法重置的ThreadAbortException。我尝试显式捕获此异常,甚至在捕获 generic 时调用,但无济于事。所以我不确定 a是否真的被调用,但无论如何,当前进程会立即结束。而且,它甚至将错误显示为 T-SQL 错误而不是 .NET Framework 错误,这很奇怪。Thread.ResetAbort
Exception
ThreadAbortException
该 Connect 错误的报告者在 SQL Server 2014 上进行了测试,我在 SQL Server 2012 上进行了测试。我不能确定 SQL Server 2016 中是否仍然存在这种行为,但我强烈怀疑它确实存在,因为似乎没有投入大量精力(如果有的话)来修复和/或改进 SQL Server 的 CLR 集成(即 SQLCLR)。因此,就目前而言,以及可能在可预见的未来,有三种可能的解决方法对我有用:
THROW
。RAISERROR(); RETURN;
我能想到的唯一缺点是:ERROR_NUMBER
即时设置自定义ERROR_NUMBER
发送给调用者将您的查询包装在 T-SQL
TRY
/CATCH
中,它使用RAISERROR
:这样做的好处是您可以继续使用
THROW
,并且当被非 SQLCLR 应用程序代码、不使用上下文连接的 SQLCLR 应用程序代码、其他存储过程、SQL 代理作业等调用时,它将按预期工作。而且您不必返回并编辑任何现有的存储过程:-)。SAFE
程序集中完成。CONTEXT_INFO
本地临时表等)SET XACT_ABORT ON 使 THROW 行为与 RAISERROR 相同。见这里 - https://stackoverflow.com/questions/17781196/throw-t-sql-exception-from-clr