Estou chamando um procedimento armazenado T-SQL de um procedimento armazenado CLR. e a THROW
instrução em T-SQL interrompe a execução do CLR sem entrar no catch
bloco. Existe algo que eu possa mudar no meu código para fazer com que o procedimento CLR execute o catch
bloco?
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);
}
}
}
Parece que esse comportamento é específico para conexões usando
"Context Connection = true;"
. Eu tentei contornar isso escrevendo atry-catch-finally
estrutura em vez de usar ausing
macro, mas isso não teve efeito.Um bug do Microsoft Connect foi registrado há quase 3 meses em relação a esse comportamento. Nesse bug do Connect, foi especulado que
THROW
gera uma ThreadAbortException que não pode ser redefinida por meio do método Thread.ResetAbort . Tentei capturar explicitamente essa exceção e até mesmo ligarThread.ResetAbort
ao capturar o genéricoException
, mas sem sucesso. Portanto, não tenho certeza se aThreadAbortException
está realmente sendo chamado, mas, independentemente disso, o processo atual termina imediatamente. E ainda exibe o erro como um erro T-SQL e não um erro .NET Framework, o que é estranho.O relator desse bug do Connect testou no SQL Server 2014 e eu testei no SQL Server 2012. Não posso dizer com certeza se esse comportamento ainda existe no SQL Server 2016, mas suspeito fortemente que sim, pois parece não haver muito esforço (se houver) para corrigir e/ou melhorar a integração CLR do SQL Server (ou seja, SQLCLR). Portanto, por enquanto, e provavelmente no futuro previsível, existem três possíveis soluções alternativas que funcionaram para mim:
THROW
porRAISERROR(); RETURN;
for procs que podem ser chamados por objetos SQLCLR. As únicas desvantagens que consigo pensar são:ERROR_NUMBER
on-the-flyERROR_NUMBER
para o chamadorEnvolva sua consulta em um T-SQL
TRY
/CATCH
que usaRAISERROR
:O benefício aqui é que você pode continuar a usar
THROW
e funcionará conforme o esperado quando chamado pelo código do aplicativo não SQLCLR, pelo código do aplicativo SQLCLR que não está usando a conexão de contexto, por outros procedimentos armazenados, em tarefas do SQL Agent etc. e você não precisa voltar e editar nenhum Stored Procedure existente :-).SAFE
Assembly.CONTEXT_INFO
, tabelas temporárias locais, etc.)SET XACT_ABORT ON torna o comportamento THROW igual ao RAISERROR. Veja aqui - https://stackoverflow.com/questions/17781196/throw-t-sql-exception-from-clr