Quero relançar a mesma exceção no SQL Server que acabou de ocorrer no meu bloco try. Consigo lançar a mesma mensagem, mas desejo lançar o mesmo erro.
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Tags.tblDomain (DomainName, SubDomainId, DomainCode, Description)
VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
declare @severity int;
declare @state int;
select @severity=error_severity(), @state=error_state();
RAISERROR(@@Error,@ErrorSeverity,@state);
ROLLBACK TRANSACTION
END CATCH
RAISERROR(@@Error, @ErrorSeverity, @state);
Esta linha mostrará erro, mas eu quero uma funcionalidade parecida com isso. Isso gera um erro com o número de erro 50000, mas quero que seja emitido o número do erro que estou passando @@error
,
Eu quero capturar esse erro não no frontend.
ie
catch (SqlException ex)
{
if ex.number==2627
MessageBox.show("Duplicate value cannot be inserted");
}
Eu quero essa funcionalidade. que não pode ser alcançado usando raiseerror
. Não quero dar mensagem de erro personalizada no back-end.
RAISEERROR
deve retornar o erro mencionado abaixo quando eu passar ErrorNo para ser lançado em catch
Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,
Linha 14 violação da restrição UNIQUE KEY 'UK_DomainCode'. Não é possível inserir uma chave duplicada no objeto 'Tags.tblDomain'. A instrução foi encerrada.
EDITAR:
Qual pode ser a desvantagem de não usar o bloco try catch se eu quiser que a exceção seja tratada no front-end, considerando que o procedimento armazenado contém várias consultas que precisam ser executadas?
raiserror
, o que é diferente de como c # sai após athrow
. Então, adicionei umreturn
dentro docatch
porque queria corresponder a esse comportamento.RAISERROR()
a documentação de . A gravidade de ≥11 somente salta para umCATCH
bloco se estiver dentro de umTRY
bloco. Portanto, você deve terBEGIN TRY…END CATCH
código próximo se quiserRAISERROR()
afetar o controle de fluxo.SQL 2012 apresenta a instrução throw:
http://msdn.microsoft.com/en-us/library/ee677615.aspx
BEGIN TRY BEGIN TRANSACTION ... COMMIT TRANSACTION END TRY BEGIN CATCH ROLLBACK TRANSACTION; THROW END CATCH
fonte
ROLLBACK
linha é importante! Sem ele, você pode obter umSQLException: Cannot roll back THROW
.Jogando novamente dentro do bloco CATCH (código pré-SQL2012, use a instrução THROW para SQL2012 e posterior):
DECLARE @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(), @ErrorNumber int = ERROR_NUMBER(), @ErrorSeverity int = ERROR_SEVERITY(), @ErrorState int = ERROR_STATE(), @ErrorLine int = ERROR_LINE(), @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-'); SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage; RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)
fonte
Eu acho que suas escolhas são:
Em algum ponto, o SQL provavelmente apresentará um comando reraise ou a capacidade de detectar apenas alguns erros. Mas, por enquanto, use uma solução alternativa. Desculpe.
fonte
Você não pode: apenas o motor pode lançar erros menores que 50000. Tudo que você pode fazer é lançar uma exceção que parece com isso ...
Veja minha resposta aqui por favor
O questionador aqui usou transações do lado do cliente para fazer o que ele queria, o que eu acho um pouco bobo ...
fonte
Ok, esta é uma solução alternativa ... :-)
DECLARE @Error_Number INT BEGIN TRANSACTION BEGIN TRY INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') /* Column 'Name' has unique constraint on it*/ END TRY BEGIN CATCH SELECT ERROR_NUMBER() --RAISERROR (@ErrorMessage,@Severity,@State) ROLLBACK TRAN END CATCH
Se você observar o bloco catch, ele não está gerando o erro, mas retornando o número real do erro (e também iria reverter a transação). Agora, em seu código .NET, em vez de capturar a exceção, se você usar ExecuteScalar (), obtém o número do erro real que deseja e mostra o número apropriado.
Espero que isto ajude,
EDITAR: - Apenas uma observação, se você deseja obter o número de registros afetados e tentando usar ExecuteNonQuery, a solução acima pode não funcionar para você. Caso contrário, acho que seria adequado para o que você precisa. Avise-se me.
fonte
A maneira de interromper a execução em um procedimento armazenado após a ocorrência de um erro e enviá-lo de volta ao programa de chamada é seguir cada instrução que possa gerar um erro com este código:
Fiquei surpreso ao descobrir que a execução em um procedimento armazenado pode continuar após um erro - não perceber isso pode levar a alguns bugs difíceis de rastrear.
Este tipo de tratamento de erros é paralelo (pré. Net) ao Visual Basic 6. Ansioso pelo comando Throw no SQL Server 2012.
fonte
Dado que você ainda não mudou para 2012, uma maneira de implementar o borbulhamento do código de erro original é usar a parte da mensagem de texto da exceção que você está (re) lançando do bloco catch. Lembre-se de que ele pode conter alguma estrutura, por exemplo, texto XML para o código do chamador analisar em seu bloco catch.
fonte
Você também pode criar um procedimento armazenado de wrapper para esses cenários quando desejar que a instrução SQL seja executada dentro da transação e alimentar o erro em seu código.
CREATE PROCEDURE usp_Execute_SQL_Within_Transaction ( @SQL nvarchar(max) ) AS SET NOCOUNT ON BEGIN TRY BEGIN TRANSACTION EXEC(@SQL) COMMIT TRANSACTION END TRY BEGIN CATCH DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE() ROLLBACK TRANSACTION RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState) END CATCH GO -- Test it EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2' EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2' EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'
fonte
Do ponto de vista do design, qual é o ponto de lançar exceções com números de erro originais e mensagens personalizadas? Até certo ponto, ele quebra o contrato de interface entre os aplicativos e o banco de dados. Se você deseja capturar erros originais e tratá-los em código superior, não os trate no banco de dados. Então, ao capturar uma exceção, você pode alterar a mensagem apresentada ao usuário para o que quiser. Eu não faria isso, porque torna o código do seu banco de dados hmm 'incorreto'. Como outros disseram, você deve definir um conjunto de seus próprios códigos de erro (acima de 50000) e jogá-los em seu lugar. Em seguida, você pode lidar com problemas de integridade ('Valores duplicados não são permitidos') separadamente de possíveis problemas de negócios - 'O código postal é inválido', 'Nenhuma linha foi encontrada correspondendo aos critérios' e assim por diante.
fonte
try { code(); } catch (Exception exc) { log(exc); throw; } finally { cleanup(); }
ondethrow;
apenas levantará a exceção original, com seu contexto original.