SQL Server:使用原始异常编号的Rethrow异常

Fab*_*iro 8 c# sql asp.net exception-handling sql-server-2008

我在存储过程中使用TRY CATCH块,其中有两个INSERT指令.

如果出现问题,CATCH块会负责回滚所做的所有更改并且工作正常,除了一件事!

我的ASP.NET应用程序捕获的异常是一个数字为50000的SqlException.这不是原始数字!(我期待的数字是2627)

在异常的Message属性中,我可以看到原始的异常编号和格式化的消息.

我怎样才能获得原始的例外号码?

try
{
    // ... code
}
catch
(SqlException sqlException)
{
    switch (sqlException.Number)
    {
        // Name already exists
        case 2627:
            throw new ItemTypeNameAlreadyExistsException();

        // Some other error
        // As the exception number is 50000 it always ends here!!!!!!
        default:
            throw new ItemTypeException();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,返回值已被使用.我想我可以使用输出参数来获取异常编号,但这是个好主意吗?

我该怎么做才能获得例外号码?谢谢

PS:这是必需的,因为我有两个INSERT指令.

gbn*_*gbn 12

您可以像这样重新抛出它:

..
END TRY
BEGIN CATCH
    DECLARE @errnum int;
    SELECT @errnum = ERROR_NUMBER();
    RAISERROR (@errnum, 16, 1);
END CATCH
Run Code Online (Sandbox Code Playgroud)

但是,由于ERROR_NUMBER()的sys.messages行中的%s等占位符,您很可能会失去意义.

你可以做这样的事情来包括数字并重新抛出原始信息

..
END TRY
BEGIN CATCH
    DECLARE @errnum nchar(5), @errmsg nvarchar(2048);
    SELECT
        @errnum = RIGHT('00000' + ERROR_NUMBER(), 5),
        @errmsg = @errnum + ' ' + ERROR_MESSAGE();
    RAISERROR (@errmsg, 16, 1);
END CATCH
Run Code Online (Sandbox Code Playgroud)

前5个字符是原始数字.

但是如果你有嵌套代码,那么你最终会得到"00123 00456错误文本".

就个人而言,我只处理SQL异常数字以将我的错误(50000)与我的代码未运行的引擎错误(例如缺少参数)分开.

最后,您可以将其传递给返回值.

我问了一个问题:SQL Server错误处理:异常和数据库 - 客户端契约


Rem*_*anu 9

如果在T-SQL中使用BEGIN TRY/BEGIN CATCH,则会丢失原始引擎引发的异常.您不应该手动引发系统错误,因此您无法重新引发原始错误号2627.T-SQL错误处理与C#/ C++错误处理不相似,没有办法重新抛出原始错误例外.存在这种限制的原因有很多,但足以说明这一点存在,你不能忽视它.

但是,只要它们高于50000范围,提出自己的错误代码就没有限制.安装应用程序时,使用sp_addmessage注册自己的消息:

exec sp_addmessage 50001, 16, N'A primary key constraint failed: %s';
Run Code Online (Sandbox Code Playgroud)

在你的T-SQL中你会引发新的错误:

@error_message = ERROR_MESSAGE();
raiserror(50001, 16, 1, @error_message;
Run Code Online (Sandbox Code Playgroud)

在C#代码中,您将查找错误号50001而不是2627:

foreach(SqlError error in sqlException.Errors)
{
 switch (error.Number)
 {
 case 50001: 
    // handle PK violation
 case 50002:
    //
 }
}
Run Code Online (Sandbox Code Playgroud)

我有一个更简单的答案,但遗憾的是这就是事情的方式.T-SQL异常处理没有将seamlesly集成到CLR异常处理中.

  • “没有办法重新抛出原始异常”——SQL Server 2012(兼容级别110)和更高版本支持“`THROW;`”,它正是这样做的。 (2认同)

Fab*_*iro 0

谢谢你们的回答。从重新引发的异常消息中获取错误是我已经做过的事情。

@gbn 我也喜欢 gbn 答案,但我会坚持这个答案,因为它是最有效的答案,我将其发布在这里希望它对其他人也有用。

答案是在应用程序中使用事务。如果我没有捕获存储过程中的异常,我将获得 SqlException 对象中的原始编号。在捕获应用程序中的原始异常后,我编写以下代码

transaction.Rollback();
Run Code Online (Sandbox Code Playgroud)

否则:

transaction.Commit();
Run Code Online (Sandbox Code Playgroud)

这比我最初预期的要简单得多!

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction.aspx