2010-03-20 28 views
62

Quiero volver a lanzar la misma excepción en el servidor sql que se ha producido en mi bloque try. Puedo lanzar el mismo mensaje pero quiero lanzar el mismo error.cómo volver a lanzar la misma excepción en el servidor sql

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 línea mostrará el error, pero quiero funcionalidad algo por el estilo. Esto genera error con número de error 50000, pero quiero número Erron a ser arrojado que estoy pasando @@error,

Quiero capturar este error hay en el frontend

es decir

catch (SqlException ex) 
{ 
if ex.number==2627 
MessageBox.show("Duplicate value cannot be inserted"); 
} 

quiero esta funcionalidad que no se puede lograr usando raiseerror. No quiero dar un mensaje de error personalizado al final.

RAISEERROR debe devolver error mencionado más adelante cuando paso ErrorNo a ser lanzado en la captura

Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert, 

Línea 14 Violación de Unique 'UK_DomainCode' CLAVE restricción. No se puede insertar la clave duplicada en el objeto 'Tags.tblDomain'. La declaración ha finalizado.

EDIT:

Cuál puede ser el inconveniente de no usar bloqueador intento de captura si quiero excepción que se maneja en frontend considerando procedimiento almacenado contiene varias consultas que necesitan ser ejecutados

Respuesta

81

Aquí hay una muestra de código de limpieza completamente funcional para revertir una serie de declaraciones si se produce un error e informar el mensaje de error.

begin try 
    begin transaction; 

    ... 

    commit transaction; 
end try 
begin catch 
    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int; 
    select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); 
    rollback transaction; 
    raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState); 
end catch 
+8

Estaba usando esto en el medio de un procedimiento almacenado, y encontré que continuaría ejecutándose después de 'raiserror', que es diferente de cómo C# sale después de un' throw'. Así que agregué un 'return' dentro del' catch' porque quería hacer coincidir ese comportamiento. –

+0

@BogdanBogdanov Revertí la edición porque el objetivo de este código es ser mínimo y no desvirtuar el código real ingresado en lugar del ... –

+0

Ok, no hay problema, @Ben Gripka. Intento hacerlo más legible en la pantalla. Gracias por indicar la razón para realizar la reversión. –

4

I piensa que sus opciones son:

  • Dont detectar el error (dejar que la burbuja hacia arriba)
  • Eleve uno personalizado

En algún momento, SQL probablemente presentará un comando de re-publicación o la capacidad de atrapar solo ciertos errores. Pero por ahora, use una solución alternativa. Lo siento.

+6

en sql 2012 puede volver a plantear una excepción con la nueva palabra clave THROW – sergiom

+5

Sí. Por supuesto, eso no estaba disponible cuando se hizo esta pregunta. –

+0

Sería más importante atrapar y lanzar un nuevo error que no captarlo y dejarlo 'burbujear' porque es probable que requiera algunas actividades de limpieza, acción correctiva y cierre para manejar la excepción adecuadamente. Un ejemplo obvio sería cerrar y deshacerse de un cursor. Otros ejemplos pueden ser ejecutar un procedimiento de registro o restablecer algunos datos. –

0

Ok, esto es una solución ... :-)

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 

Si se observa el bloque catch, No es elevar el error pero volviendo el número de error real (y también podría deshacer la transacción) . Ahora, en su código .NET, en lugar de detectar la excepción , si usa ExecuteScalar(), obtendrá el número de error real que desea y mostrará el número apropiado.

int errorNumber=(int)command.ExecuteScalar(); 
if(errorNumber=<SomeNumber>) 
{ 
    MessageBox.Show("Some message"); 
} 

Espero que esto ayude,

EDIT: - Sólo una nota, si desea obtener el número de registros afectados y tratando de utilizar ExecuteNonQuery, la solución anterior puede no funcionar para usted.De lo contrario, creo que se adaptaría a lo que necesita. Házmelo saber.

+0

@Ashish Gupta: Thx por ayuda, pero necesito que se arroje una excepción de la base de datos a la interfaz, de lo contrario tengo muchas opciones abiertas como print error_number(), return error_number y el 1 u sugerido –

1

Usted no puede: sólo el motor puede arrojar errores menores al 50000. Todo lo que se puede hacer es lanzar una excepción que se ve igual ...

See my answer here please

La pregunta aquí utilizado transacciones del lado del cliente para hacer lo que quería que creo que es un poco tonto ...

-3

Desde el punto de vista del diseño, ¿qué sentido tiene arrojar excepciones con números de error originales y mensajes personalizados? Hasta cierto punto, rompe el contrato de interfaz entre las aplicaciones y la base de datos. Si desea detectar errores originales y manejarlos en un código más alto, no los maneje en la base de datos. Luego, cuando atrape una excepción, puede cambiar el mensaje presentado al usuario a cualquier cosa que desee. Sin embargo, no lo haría, porque hace que el código de su base de datos hmm 'no sea correcto'. Como otros dijeron, debes definir un conjunto de tus propios códigos de error (por encima de 50000) y lanzarlos en su lugar. A continuación, puede solucionar los problemas de integridad ('Duplicar valores no están permitidos') por separado de posibles problemas comerciales: 'Código postal no es válido', 'No se encontraron filas que coincidan con los criterios', etc.

+9

¿De qué sirve arrojar excepciones con ¿números de error originales y mensajes personalizados? Supongamos que desea manejar uno o dos errores específicos (esperados) directamente en el bloque catch y dejar el resto para las capas superiores. Por lo tanto, debe ser capaz de volver a plantear las excepciones que no manejó ... preferiblemente sin tener que recurrir a informar y manejar los errores de alguna otra manera especial. – Jenda

+1

Además de lo que explicó @Jenda, me gusta usar try-catch para garantizar que la ejecución del código no continúe después de una excepción, al igual que hacerlo en C#: 'try {code(); } catch (Exception exc) {log (exc); lanzar; } finally {cleanup(); } ', donde' throw; 'levantará la excepción original, con su contexto original. –

+0

Capturo errores y vuelvo a lanzar mensajes de error personalizados en SQL para agregar detalles que describen la línea en la que ocurrió el error u otros detalles (como los datos que intentan insertarse) para ayudarme a rastrear el error más adelante. –

91

SQL 2012 introduce la instrucción throw:

http://msdn.microsoft.com/en-us/library/ee677615.aspx

Si no se especifica la sentencia throw sin parámetros, debe aparecer dentro de un bloque CATCH. Esto provoca que la excepción atrapada se eleve.

BEGIN TRY 
    BEGIN TRANSACTION 
    ... 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION; 
    THROW 
END CATCH 
+2

Mucho más limpio y legible que a la vieja usanza. –

+2

Cuidado, parece que esta solución solo funciona desde el servidor sql 2012 y superior: https://msdn.microsoft.com/en-us/library/ee677615.aspx – Adi

+0

¿Por qué necesitamos 'THROW', la excepción ya ha sido lanzada? –

0

La manera de detener la ejecución en un procedimiento almacenado después de que ha ocurrido un error y la burbuja el error de nuevo al programa de llamada es seguir cada declaración que podría producir un error con este código:

If @@ERROR > 0 
Return 

Me sorprendí a mí mismo al descubrir que la ejecución en un procedimiento almacenado puede continuar después de un error, sin darse cuenta de esto puede dar lugar a algunos errores difíciles de rastrear.

Este tipo de paralelos de tratamiento de errores (pre) .Net Visual Basic 6. Mirando hacia adelante a la orden del tiro en SQL Server 2012.

0

Teniendo en cuenta que no se ha mudado a 2012, sin embargo, una manera de poner en práctica el El borboteo del código de error original es usar la parte del mensaje de texto de la excepción que está (re) lanzando desde el bloque catch. Recuerde que puede contener alguna estructura, por ejemplo, texto XML para que su código de llamada pueda analizarse en su bloque catch.

4

Regeneración de dentro del bloque CATCH (código de pre-SQL2012, sentencia use lanzar para SQL2012 y posteriores):

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) 
0

También puede crear un contenedor procedimiento almacenado para los esos escenarios cuando desee que la instrucción SQL para ser ejecutado dentro de la transacción y alimentar el error hasta su 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' 
Cuestiones relacionadas