微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Try...catch 在围绕存储过程执行时不起作用

如何解决Try...catch 在围绕存储过程执行时不起作用

在我的 web api 数据访问层中,Asp.net TRY CATCH 没有捕获存储过程的失败。注意:此代码模板用于丢弃我的 web api 数据访问层并且工作正常。只是在这种情况下不是。奇怪的!! (我在底部包含了一个类似且有效的示例)。

我希望存储过程失败,因为没有符合条件的行。因此,在这种情况下,我会引发错误 (RAISERROR) 并将一个条目写入我的错误日志表。

当我通过 SSMS 执行它或运行调用执行存储过程的 Web api 的 Web 应用程序时会发生这种情况。

包含 2 个条目的错误日志表。 1 来自通过 SSMS 运行存储过程,另一个来自运行我的网络应用程序。

enter image description here

问题是web api数据访问层代码执行没有捕捉到存储过程返回的错误。它不会进入 CATCH。

enter image description here

这是从我的 web api 控制器调用的 web api 数据访问层函数。它的 TRY CATCH 无法正常工作:

public List<BlogPublishedCategory> 
GetBlogCategorysInBlogsPublishedList(string userName,string ipAddress)
{
   string userFriendlyMessage = "Unable to get the blog categorys in the 
   blogs published list. We have been notified and are working to resolve 
   this. Please do not continue.";

    List<BlogPublishedCategory> blogPublishedCategoryList = new 
    List<BlogPublishedCategory>();

    sqlDataReader blogCategorysInBlogsDataReader = null;

    try
    {
        dbFunc.OpenDB();

        sqlCommand cmd = new sqlCommand("dbo.GetBlogCategorysInBlogsPublished",dbFunc.objConn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Clear();

        cmd.Parameters.AddWithValue("@a_UserName",userName);
        cmd.Parameters.AddWithValue("@a_IpAddress",ipAddress);

        blogCategorysInBlogsDataReader = cmd.ExecuteReader();

        while (blogCategorysInBlogsDataReader.Read())
        {
            // Add to the list of BlogPublishedCategory - creates a new row for the collection.
            blogPublishedCategoryList.Add(new BlogPublishedCategory
            {
                BlogCategoryId = Convert.ToInt32(blogCategorysInBlogsDataReader["BlogCategoryId"]),BlogCategoryDescr = blogCategorysInBlogsDataReader["BlogCategoryDescr"].ToString(),});
        }

        // Return the blogPublishedCategoryList object.
        return blogPublishedCategoryList;
    }
    catch (sqlException sqlex)
    {
    if (sqlex.Message.Contains("Critical"))
        {
            currentDateTime = DateTime.Now;
            senDalertEmailResult = SenDalertEmailToStaff(currentDateTime,userName,ipAddress);

            if (senDalertEmailResult == "")
            {
                throw new Exception(userFriendlyMessage);
            }
            else
            {
                throw new Exception("In DataAccessLayer/GetBlogCategorysInBlogsPublishedList(). Sending an alert email for the initial sql exception error: " + sqlex.Message + ". Now getting this error: " + senDalertEmailResult);
            }
        }
        else
        {
            errorMessage = "sql Exception Error in DataAccessLayer/GetBlogCategorysInBlogsPublishedList(). Using 'GetBlogCategorysInBlogsPublished' s/p. Error: " + sqlex.Message;

            currentDateTime = DateTime.Now;
            processErrorLogAndSenDalertEmailResult = ProcessErrorLogAndSenDalertEmail(currentDateTime,errorMessage,additionalInfoForLog,ipAddress);

            if (processErrorLogAndSenDalertEmailResult != "")
            {
                throw new Exception("Error in DataAccessLayer/GetBlogCategorysInBlogsPublishedList(). Using 'GetBlogCategorysInBlogsPublished' s/p. Logging the initial sql exception error: " + sqlex.Message + ". Now getting this error: " + processErrorLogAndSenDalertEmailResult);
            }
            else
            {
                throw new Exception(userFriendlyMessage);
            }
        }
    }
    catch (Exception ex)
    {
        errorMessage = "Error in DataAccessLayer/GetBlogCategorysInBlogsPublishedList(). Using 'GetBlogCategorysInBlogsPublished' s/p. Error: " + ex.Message;

        currentDateTime = DateTime.Now;
        processErrorLogAndSenDalertEmailResult = ProcessErrorLogAndSenDalertEmail(currentDateTime,ipAddress);

        if (processErrorLogAndSenDalertEmailResult != "")
        {
            throw new Exception("Error in DataAccessLayer/GetBlogCategorysInBlogsPublishedList(). Using 'GetBlogCategorysInBlogsPublished' s/p. Logging the initial error: " + ex.Message + ". Now getting this error: " + processErrorLogAndSenDalertEmailResult);
        }
        else
        {
            throw new Exception(userFriendlyMessage);
        }
    }
    finally
    {
        if (blogCategorysInBlogsDataReader != null)
        {
            blogCategorysInBlogsDataReader.Close();
        }

        dbFunc.CloseDB();
    }
}

GetBlogCategorysInBlogsPublished 存储过程(由于行数为 0,因此失败):

USE [DBGbngDev]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo]. 
[GetBlogCategorysInBlogsPublished]') AND type in (N'P',N'PC'))
    BEGIN
       DROP PROCEDURE [dbo].GetBlogCategorysInBlogsPublished
    END
GO

CREATE procedure [dbo].[GetBlogCategorysInBlogsPublished] 

@a_UserName              varchar(250) = NULL,@a_IpAddress             varchar(250) = NULL

AS 
BEGIN

DECLARE @RowCount                  int,@ReturnCode                int,@CurrentDateTime           datetime,@Message                   varchar(max) = '',@ApiMessageOut             varchar(max),@ApiAccessSwitchOut        bit

DECLARE @ErrorLine          AS INT;
DECLARE @ErrorMessage       AS VARCHAR(2048);
DECLARE @ErrorNumber        AS INT; 
DECLARE @ErrorSeverity      AS INT; 
DECLARE @ErrorState         AS INT; 
DECLARE @DatabaseName       AS VARCHAR(255);
DECLARE @ServerName         AS VARCHAR(255);
DECLARE @ErrorDescription   AS VARCHAR(MAX);
DECLARE @CRLF               AS VARCHAR(2);

SELECT @CurrentDateTime = GETDATE()
    
BEGIN TRY

   SET NOCOUNT ON;

   IF ( ( @a_UserName  = '' OR @a_UserName  IS NULL ) 
   OR ( @a_IpAddress = '' OR @a_IpAddress IS NULL ) )
      BEGIN
         SELECT @Message = 'Critical Error - procedure GetBlogCategorysInBlogsPublished - invalid 
         parameters. They cannot be null or empty.' 

         IF ( @a_UserName = '' OR @a_UserName IS NULL )
             BEGIN
                SET @a_UserName = 'No "user name" parameter provided.'
             END

         IF ( @a_IpAddress = '' OR @a_IpAddress IS NULL )
             BEGIN
                SET @a_IpAddress = 'No "ip address" parameter provided.'
             END

        RAISERROR (@Message,16,1)
      END
   ELSE
      BEGIN
           -- Do the API security check. If this user is valid,you can continue with further
           processing.
           SELECT @ReturnCode = -1
           EXECUTE @ReturnCode = dbo.GetApiAccess
                              @CurrentDateTime,@a_UserName,@a_IpAddress,@a_ApiAccessSwitchFromGet = @ApiAccessSwitchOut OUTPUT,@a_ApiMessageFromGet = @ApiMessageOut OUTPUT

           IF @ReturnCode = -1
                BEGIN 
                   RAISERROR ('Critical Error - procedure GetBlogCategorysInBlogsPublished Failed during execute of procedure GetApiAccess.',1 )
                END
    
           -- Web api access was granted. 
           IF @ApiAccessSwitchOut = 1
               BEGIN    
                   SELECT disTINCT (a.BlogCategoryId) as BlogCategoryId,a.BlogCategoryDescr as BlogCategoryDescr
                   FROM dbo.BlogCategory a
                   JOIN dbo.Blog b On ( a.BlogCategoryId = b.BlogCategoryId )
                   WHERE ( b.PublishSwitch = 1 AND b.CanBeSeenSwitch = 1 )
                   ORDER BY a.BlogCategoryId asc  

                   SELECT @ReturnCode = @@ERROR,@RowCount = @@ROWCOUNT

                   IF @ReturnCode <> 0
                       BEGIN 
                           SELECT @Message = 'Critical Error - procedure GetBlogCategorysInBlogsPublished Failed during the select.'
                           RAISERROR (@Message,1)
                       END
         
                   IF @RowCount = 0
                       BEGIN
                           SELECT @Message =  'Critical Error - procedure GetBlogCategorysInBlogsPublished Failed during the select. There are no BlogCategory entries.' 
                           RAISERROR (@Message,1)
                       END  
               END
           ELSE
               BEGIN
                    -- Web api access was NOT granted. The user did not have permission to use the web api or there is an error in the GetApiAccess procedure.
                    RAISERROR (@ApiMessageOut,1 )
              END
      END
    
   -- Returns success.
   RETURN 0

END TRY

BEGIN CATCH
    SELECT 
          @ErrorLine = ERROR_LINE()
          -- ERROR_MESSAGE() contains the RAISERROR message raised above.,@ErrorMessage = ERROR_MESSAGE(),@ErrorNumber = ERROR_NUMBER(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE(),@DatabaseName = CAST(DB_NAME() AS VARCHAR),@ServerName = CAST(SERVERPROPERTY ( 'ServerName' ) AS VARCHAR),@CRLF  = CHAR(13) + CHAR(10)

        SET @ErrorDescription = 'From stored procedure: '  + ERROR_PROCEDURE() 
                                + '. Error Line: '  + CAST(@ErrorLine AS VARCHAR)
                                + '. Error Message: ' + @ErrorMessage
                                + ' Error Number: ' + CAST(@ErrorNumber AS VARCHAR)
                                + '. Error Severity: ' + CAST(@ErrorSeverity AS VARCHAR)
                                + '. Error State: ' + CAST(@ErrorState AS VARCHAR)
                                + '. Database Name: '  + @DatabaseName
                                + '. Server Name: ' + @ServerName

    IF (XACT_STATE() <> 0)
        BEGIN
           ROLLBACK TRANSACTION 
        END
 
    IF (@ErrorSeverity = 16) AND (@ErrorState = 2)
        BEGIN 
           RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState)
        END      
    ELSE
        BEGIN 
           -- Log the critical error.   
           BEGIN TRY
              EXEC dbo.InsertBlogErrorLog
                         @a_LogDateTime = @CurrentDateTime,@a_UserName = @a_UserName,@a_UserIpAddress = @a_IpAddress,@a_ObjectID = @@PROCID,@a_MessageType = 'S/P Critical Error',@a_LogMessage = @ErrorDescription           
           END TRY

           BEGIN CATCH
              -- Stack the messages.
              SELECT @Message = 'Critical Error - procedure InsertBlogErrorLog Failed. A log entry cannot be made. Do not continue. Contact IT. Initial error message: ' + @ErrorMessage
              RAISERROR(@Message,1) 
           END CATCH

           SELECT @message = 'Critical Error - do not continue. Contact IT and provide this log date: ' + Convert(VARCHAR,@CurrentDateTime,21)
           RAISERROR(@Message,1)                 
        END

     -- Returns failure.
     RETURN 1      

END CATCH
END

这是一个类似的例子,我在存储过程中强制出现错误,以表明类似(几乎相同)的 web api 数据访问层函数确实捕获了存储过程错误

带有条目的错误日志表。从运行我的 web api。

enter image description here

DOES 捕获存储过程返回的错误的 web api 数据访问层代码执行。它确实进入 CATCH 并执行我期望的处理。

enter image description here

这是从我的 web api 控制器调用的 web api 数据访问层函数。它具有正常工作的 TRY CATCH:

public List<BlogCategory> GetBlogCategoryList(string userName,string ipAddress)
    {
        string userFriendlyMessage = "Unable to get blog categories. We have been notified and are working to resolve this. Please do not continue.";

        List<BlogCategory> blogCategoryList = new List<BlogCategory>();

        sqlDataReader blogCategoryDataReader = null;

        try
        {
            dbFunc.OpenDB();

            sqlCommand cmd = new sqlCommand("dbo.GetBlogCategory",dbFunc.objConn);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Clear();

            cmd.Parameters.AddWithValue("@a_UserName",userName);
            cmd.Parameters.AddWithValue("@a_IpAddress",ipAddress);

            blogCategoryDataReader = cmd.ExecuteReader();

              while (blogCategoryDataReader.Read())
            {
                blogCategoryList.Add(new BlogCategory
                {
                    BlogCategoryId = Convert.ToInt32(blogCategoryDataReader["BlogCategoryId"]),BlogCategoryDescr = blogCategoryDataReader["BlogCategoryDescr"].ToString(),});
            }

            return blogCategoryList;
        }
        catch (sqlException sqlex)
        {
            if (sqlex.Message.Contains("Critical"))
            {
                // A "critical" error coming from the stored procedure.

                // So,send an alert email to a staff member (an Admin),but do NOT process the error log as it has been done already 
                // in the stored procedure.

                currentDateTime = DateTime.Now;
                senDalertEmailResult = SenDalertEmailToStaff(currentDateTime,ipAddress);

                if (senDalertEmailResult == "")
                {
                    throw new Exception(userFriendlyMessage);
                }
                else
                {
                    throw new Exception("In DataAccessLayer/GetBlogCategoryList(). Sending an alert email for the initial sql exception error: " + sqlex.Message + ". Now getting this error: " + senDalertEmailResult);
                }
            }
            else
            {
                // Not coming from the stored procedure. Like if the stored procedure above was not named properly,does not exist,parameter missing,etc.
                errorMessage = "sql Exception Error in DataAccessLayer/GetBlogCategoryList(). Using 'GetBlogCategory' s/p. Error: " + sqlex.Message;

                // Log the error and send an alert email.
                currentDateTime = DateTime.Now;
                processErrorLogAndSenDalertEmailResult = ProcessErrorLogAndSenDalertEmail(currentDateTime,ipAddress);

                if (processErrorLogAndSenDalertEmailResult != "")
                {
                    throw new Exception("Error in DataAccessLayer/GetBlogCategoryList(). Using 'GetBlogCategory' s/p. Logging the initial sql exception error: " + sqlex.Message + ". Now getting this error: " + processErrorLogAndSenDalertEmailResult);
                }
                else
                {
                    throw new Exception(userFriendlyMessage);
                }
            }
        }
        catch (Exception ex)
        {
            errorMessage = "Error in DataAccessLayer/GetBlogCategoryList(). Using 'GetBlogCategory' s/p. Error: " + ex.Message;

            currentDateTime = DateTime.Now;
            processErrorLogAndSenDalertEmailResult = ProcessErrorLogAndSenDalertEmail(currentDateTime,ipAddress);

            if (processErrorLogAndSenDalertEmailResult != "")
            {
                throw new Exception("Error in DataAccessLayer/GetBlogCategoryList(). Using 'GetBlogCategory' s/p. Logging the initial error: " + ex.Message + ". Now getting this error: " + processErrorLogAndSenDalertEmailResult);
            }
            else
            {
                throw new Exception(userFriendlyMessage);
            }
        }
        finally
        {
            if (blogCategoryDataReader != null)
            {
                blogCategoryDataReader.Close();
            }

            dbFunc.CloseDB();
        }
    }

这是 GetBlogCategory 存储过程(它失败了,因为我强制设置了 rowcount = 0 ):

USE [DBGbngDev]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo]. 
[GetBlogCategory]') AND type in (N'P',N'PC'))
BEGIN
   DROP PROCEDURE [dbo].GetBlogCategory
END
GO

CREATE procedure [dbo].[GetBlogCategory]

@a_UserName         varchar(250) = NULL,@a_IpAddress        varchar(250) = NULL 

AS 
BEGIN

  DECLARE @RowCount                  int,@ApiAccessSwitchOut        bit

SELECT @CurrentDateTime = GETDATE()

DECLARE @ErrorLine      AS INT;
DECLARE @ErrorMessage       AS VARCHAR(2048);
DECLARE @ErrorNumber        AS INT; 
DECLARE @ErrorSeverity      AS INT; 
DECLARE @ErrorState     AS INT; 
DECLARE @DatabaseName       AS VARCHAR(255);
DECLARE @ServerName     AS VARCHAR(255);
DECLARE @ErrorDescription   AS VARCHAR(MAX);
DECLARE @CRLF           AS VARCHAR(2);
    
BEGIN TRY

   SET NOCOUNT ON;
   IF ( ( @a_UserName  = '' OR @a_UserName  IS NULL ) OR ( @a_IpAddress = '' OR @a_IpAddress IS NULL ) )
      BEGIN
         SELECT @Message = 'Critical Error - procedure GetBlogCategory - invalid parameters. They cannot be null or empty.' 

         IF ( @a_UserName = '' OR @a_UserName IS NULL )
             BEGIN
                SET @a_UserName = 'No "user name" parameter provided.'
             END

         IF ( @a_IpAddress = '' OR @a_IpAddress IS NULL )
             BEGIN
                SET @a_IpAddress = 'No "ip address" parameter provided.'
             END

        RAISERROR (@Message,you can continue with further processing.
           SELECT @ReturnCode = -1
           EXECUTE @ReturnCode = dbo.GetApiAccess
                              @CurrentDateTime,@a_ApiMessageFromGet = @ApiMessageOut OUTPUT

           IF @ReturnCode = -1
                BEGIN 
                   RAISERROR ('Critical Error - procedure GetBlogCategory Failed during execute of procedure GetApiAccess.',1 )
                END
    
           -- Web api access was granted. 
           IF @ApiAccessSwitchOut = 1
               BEGIN
                   --SELECT BlogCategoryId as BlogCategoryId
                            --,BlogCategoryDescr as BlogCategoryDescr
                   --FROM dbo.BlogCategory
                   --ORDER BY BlogCategoryDescr asc  

                   --SELECT @ReturnCode = @@ERROR,--                @RowCount = @@ROWCOUNT

                   --IF @ReturnCode <> 0
                      -- BEGIN 
                         --  SELECT @Message = 'Critical Error - procedure GetBlogCategory Failed during the select.'
                         --  RAISERROR (@Message,1)
                      -- END
             
                   SET @RowCount = 0

                   IF @RowCount = 0
                       BEGIN
                           SELECT @Message =  'Critical Error - procedure GetBlogCategory Failed during the select. There are no entries.' 
                           RAISERROR (@Message,1)
                       END  
               END
           ELSE
               BEGIN
                    -- Web api access was NOT granted. The user did not have permission to use the web api or there is an error in the GetApiAccess procedure.
                    -- Pass the message returned from the GetApiAccess procedure.
                    RAISERROR (@ApiMessageOut,1 )
              END
      END
    
   -- Returns success.
   RETURN 0

END TRY

BEGIN CATCH

    SELECT 
            @ErrorLine = ERROR_LINE()
        -- ERROR_MESSAGE() contains the RAISERROR message raised above.,@CRLF  = CHAR(13) + CHAR(10)


        SET @ErrorDescription = 'From stored procedure: '  + ERROR_PROCEDURE() 
                                + '. Error Line: '  + CAST(@ErrorLine AS VARCHAR)
                                            + '. Error Message: ' + @ErrorMessage
                        + ' Error Number: ' + CAST(@ErrorNumber AS VARCHAR)
                    + '. Error Severity: ' + CAST(@ErrorSeverity AS VARCHAR)
                        + '. Error State: ' + CAST(@ErrorState AS VARCHAR)
                        + '. Database Name: '  + @DatabaseName
                    + '. Server Name: ' + @ServerName

    IF (XACT_STATE() <> 0)
        BEGIN
           ROLLBACK TRANSACTION 
        END
 
    IF (@ErrorSeverity = 16) AND (@ErrorState = 2)
        BEGIN 
           RAISERROR(@ErrorMessage,@ErrorState)
        END      
     ELSE 
        BEGIN 
           -- Log the critical error.   
           BEGIN TRY
              EXEC dbo.InsertBlogErrorLog
                @a_LogDateTime = @CurrentDateTime,@a_LogMessage = @ErrorDescription            
           END TRY

           BEGIN CATCH
              -- Stack the messages.
              SELECT @Message = 'Critical Error - procedure InsertBlogErrorLog Failed. A log entry cannot be made. Do not continue. Contact IT. Initial error message: ' + @ErrorMessage
              RAISERROR(@Message,1)                 
        END

     -- Returns failure.
     RETURN 1      

END CATCH
END

Dan...我把我原来的 C# 循环代码放回去,我改变了失败的 s/p GetBlogCategorysInBlogsPublished,我完全删除了 SELECT disTINCT 子句,只是通过设置 @Rowcount = 0 来强制 RAISERROR。TRY CATCH 在web api 函数将其提取并正确处理。

enter image description here

然后我在 SSMS 中使用 SELECT disTINCT 并运行它。 在“结果”选项卡中,我看到了 2 个结果,即列标题错误消息列。

这 2 个结果可能是问题所在吗?

enter image description here

在消息选项卡中,我得到:消息 50000,级别 16,状态 1,第 26 行 严重错误 - 过程 GetBlogCategorysInBlogsPublished 在选择过程中失败。没有博客类别条目。

enter image description here

然后我在 SSMS 中,在没有 SELECT 的情况下强制错误。 在“结果”选项卡中,我看到只有 1 列。

enter image description here

enter image description here

解决方法

确保使用数据访问层中的所有结果集以确保引发异常。使用 NextResult() 的示例模式:

do {
    while (blogCategoryDataReader.Read())
    {
        blogCategoryList.Add(new BlogCategory
        {
            BlogCategoryId = Convert.ToInt32(blogCategoryDataReader["BlogCategoryId"]),BlogCategoryDescr = blogCategoryDataReader["BlogCategoryDescr"].ToString(),});
            }
} while blogCategoryDataReader.NextResult();

原因是生成的异常消息RAISERROR落后于底层表格数据流中SELECT查询生成的结果集。这实际上变成了客户端 API 必须使用的多个结果集,因此客户端应用程序将所有结果使用到 ensure exceptions are raised and can be detected 非常重要。

请注意,SSMS 使用 FireInfoMessagesOnUserErrors 连接属性而不是 try/catch 来显示用户错误以及其他上下文信息。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。