SqlException转换为自定义异常处理程序

时间:2010-10-12 14:11:57

标签: .net exception-handling

我有一个现有的应用程序,它使用MS SQL存储过程来强制执行某些业务规则。当检测到错误时,使用RAISERROR将其作为异常引发回我的.Net应用程序。

然后,.Net应用程序可以使用Try / Catch块来捕获和异常以及执行和业务逻辑。问题是在单个存储过程中验证了多个业务规则。这可能引发不同的例外。捕获这些SQL异常并将其转换为自定义.Net异常处理程序的最佳方法是什么。

例如,我的存储过程可能会为RuleA和RuleB抛出异常。在我的.Net代码中,我只能捕获SqlException。我在SqlException内部异常中返回RuleA或RuleB的自定义错误消息。我可以解析Message字符串,但这是UGLY,如果有人更改了存储过程中的实现。我的逻辑不会接受它。

将通用SqlException转换为MyRuleAException或MyRuleBException的首选方法是什么?

3 个答案:

答案 0 :(得分:3)

通常,这样做的方法是在.Net代码中定义错误常量,然后检查异常处理代码中的值。您可以使用常量使代码更具可读性,如下所示:

/// <summary>
/// Represents the error code returned from stored procedure when entity could not be found.
/// </summary>
private const int SQL_ERROR_CODE_ENTITY_NOT_FOUND = 50001;

/// <summary>
/// Represents the error code returned from stored procedure when entity to be updated has time mismatch.
/// </summary>
private const int SQL_ERROR_CODE_TIME_MISMATCH = 50002;

/// <summary>
/// Represents the error code returned from stored procedure when a persistence exception occurs (ex.
/// billing flag is invalid, child records exist which prevent a delete, etc.).
/// </summary>
private const int SQL_ERROR_CODE_PERSISTENCE_ERROR = 50003;

然后,您可以处理这样的异常,它使您的代码更具可读性和可维护性:

    if (e.InnerException is SqlException)
    {
        // verify exception code from SP and throw proper exception if required
        var sqlException = (SqlException)e.InnerException;
        if (sqlException.Number == SQL_ERROR_CODE_ENTITY_NOT_FOUND)
        {
            e = new EntityNotFoundException(e.Message, e);
        }
        else if (sqlException.Number == SQL_ERROR_CODE_TIME_MISMATCH)
        {
            e = new EntityTimestampMismatchException(e.Message, e);
        }
        else if (sqlException.Number == SQL_ERROR_CODE_PERSISTENCE_ERROR)
        {
            e = new EntityServicePersistenceException(e.Message, e);
        }
    }

这在我看来就像你可以做到的一样干净,但它仍然可以,因为你在一个地方定义了错误代码,所以如果有任何变化,你只需改变一个常数。

要提出错误,您可以在T-SQL中执行以下操作:

 -- record wasn't found, raise an error
 DECLARE @l_error NVARCHAR(1000)
 SET @l_error = 'Record with ' + @p_IdFieldName + ' = ' + CONVERT(VARCHAR(128), @p_id)
    + ' does not exist in table [' + @p_TableName + ']'
 EXEC sp_addmessage @msgnum=50001, @severity=16, @msgtext=@l_error, @replace='replace'
 RAISERROR(50001, 16, 1)

50001代表SqlException.Number中的错误编号。

答案 1 :(得分:1)

我同意dcp。该过程要求您生成常量列表,并且要实现有点长的过程。但是很容易维护。

答案 2 :(得分:0)

发出错误时可以指定msg_id吗?如果是这样,我相信这可以在SqlException.Number成员中找到。然后你可以做一个if / else。我只是确保在存储过程中很好地记录它。

<强>更新

仔细观察我认为在调用RAISERROR时指定不同的错误级别然后通过SqlException.Class成员检查该级别可能会更好。例如:

--Rule A
RAISERROR (N'Rule A violation.', -- Message text.
           10, -- Severity,
           1, -- State)

--Rule B
RAISERROR (N'Rule B violation.', -- Message text.
           9, -- Severity,
           1, -- State)

--Rule C
RAISERROR (N'Rule C violation.', -- Message text.
           8, -- Severity,
           1, -- State)

然后在代码中:

catch(SqlException qex)
{
  if(qex.Class == 10){}
  else if(qex.Class == 9){}
  else if(qex.Class == 8){}
}