【问题标题】:SQL Server and how to handle the type of error without conflictSQL Server 与如何处理错误类型不冲突
【发布时间】:2011-07-15 11:11:50
【问题描述】:

我使用 SQL Server 2008 和 ASP.NET C#,并且我有一个返回一些计算行的存储过程。

我想在执行该过程之前检查参数值,如果参数值错误,我想返回自定义错误消息。

在UI方面,我必须根据错误类型返回不同的文本并使用不同的窗口。示例:

  • 参数值错误时出错。
  • 未处理的错误。

我目前在数据库中使用这种SP,但我尝试使用状态参数甚至严重性参数来识别错误类型。但是我与返回与我的参数错误相同的状态编号的未处理错误存在一些冲突,因此出现了错误的窗口。如果我使用严重性级别,我猜 SP 在某些情况下也可能返回具有相同严重性编号的错误。

我给你一个简单的例子,以便更好地了解:

CREATE PROCEDURE dbo.GetData
    @Date1 date,
    @Date2 date
AS
BEGIN

    -- check the parameters
    IF @Date2 < @Date1
    BEGIN
        RAISERROR(N'Date 2 cannot be less than Date 1', 16, 2); -- <= Here State 2
        return
    END

    -- process here...
    DECLARE @Table1 TABLE ( name nvarchar(50) NOT NULL)

    -- Supposing you have to insert a row with a NULL value
    INSERT INTO @Table1 VALUES (NULL);
    -- Thus, this query returns this error with the state 2 as well!
    --Msg 515, Level 16, State 2, Procedure GetData, Line 21
    --Cannot insert the value NULL into column 'name', table '@Table1'; column does not allow nulls. INSERT fails.

    SELECT 'Record 1';
    SELECT 'Record 2';
END

来自 c#:

List<string> data = new List<string>();

protected void Button1_Click(object sender, EventArgs e)
{
    string errorMessage = string.Empty;
    bool isErrorFromChecking = false;

    if (GetValues(ConfigurationManager.ConnectionStrings["TestConnectionString"].ConnectionString,
                new DateTime(2011, 01, 01), new DateTime(2011, 02, 01),
                ref isErrorFromChecking, ref errorMessage))
    {
        Label1.Text = data[0].ToString();
        return;
    }

    if (isErrorFromChecking)
    {
        Label1.Text = errorMessage;
        return;
    }
    Label1.Text = string.Format("Internal Error: {0}.", errorMessage);
}

private bool GetValues(string connectionString, DateTime date1, DateTime date2,
            ref bool isErrorFromChecking, ref string errorMessage)
{
    data = new List<string>();
    try
    {
        using (SqlConnection sqlCon = new SqlConnection(connectionString))
        {
            sqlCon.Open();
            SqlCommand sqlCmd = new SqlCommand();
            sqlCmd.Connection = sqlCon;
            sqlCmd.CommandType = CommandType.StoredProcedure;
            sqlCmd.CommandText = "dbo.GetData";

            sqlCmd.Parameters.AddWithValue("Date1", date1);
            sqlCmd.Parameters.AddWithValue("Date2", date2);

            SqlDataReader reader = sqlCmd.ExecuteReader();
            while (reader.Read())
            {
                data.Add(reader[0].ToString());
            }
            reader.Close();
            sqlCon.Close();
        }
    }
    catch (SqlException ex)
    {
        if (ex.State == 2)
        {
            isErrorFromChecking = true;
            errorMessage = ex.Message;
            return false;
        }
        isErrorFromChecking = false;
        errorMessage = ex.Message;
        return false;
    }
    catch (Exception ex)
    {
        isErrorFromChecking = false;
        errorMessage = ex.Message;
        return false;
    }

    return true;
}

在上面的代码中,日期是正确的,但程序没有返回消息“内部错误:...”,尽管 SP 有错误。

我有一些想法,但我只是想知道你的观点和最好的方法。

谢谢。

【问题讨论】:

    标签: c# sql-server-2008 raiserror


    【解决方案1】:

    根据这个http://msdn.microsoft.com/en-us/library/ms178592.aspx,当您自己提供消息字符串时,您将获得一个消息ID 50000。我想你可以在你的 C# 代码中进行测试。

    或者您可以在 SP 中指定一个 MsgID(值 > 50000),并根据客户端代码中的 MsgID 提供错误消息。

    【讨论】:

      【解决方案2】:

      只需将逻辑一分为二。我的意思是在一个存储过程中具有“参数检查”逻辑,其余的在另一个 SP 中。 在“检查”SP 中,使用输出参数来获取您想要返回的任何代码或错误文本。

      第二个 SP 也可以先调用 SP 以确保它不会收到错误数据。

      C# 应首先调用“检查”SP 并查看返回的内容。

      另一种方法是对错误条件使用输出参数并返回一个空数据集。

      CREATE PROCEDURE spTest (@param1 int,   @ErrorText varchar(50) OUTPUT )
      AS
      BEGIN
          SET @ErrorText = NULL
          IF @Param1 = 1 
          BEGIN
              SET @ErrorText = 'I really cant except 1 as a parameter'
              SELECT NULL as QueryPlaceholderCol
          END 
          ELSE
          BEGIN
              SELECT 3.141 AS QueryPlaceholderCol
          END
      END
      

      在 T-SQL 中测试:

      DECLARE @ReturnedError VARCHAR(50)
      EXEC spTest 1,@ReturnedError OUTPUT
      SELECT CASE WHEN @ReturnedError IS NULL THEN 'Worked Fine' ELSE @ReturnedError END AS Outputs
      EXEC spTest 1423,@ReturnedError OUTPUT
      SELECT CASE WHEN @ReturnedError IS NULL THEN 'Worked Fine' ELSE @ReturnedError END AS Outputs
      

      【讨论】:

      • 所以在你的建议中,'Check' SP 被调用了 2 次。但这是个好主意!有什么办法可以将“检查”逻辑保持在与流程相同的 SP 中?我的意思是有时在 sp 的开头比较两个日期并不容易,它可能在 sp 的结尾。
      • 是的,在我的例子中它被调用了两次。当然,如果 SP 没有在其他地方使用,您可以完全从 SP 中删除支票并依赖客户端应用程序始终首先调用支票。我已经扩展了答案以提供另一种方法
      • 感谢@Joel 的回答。实际上,我终于像您的替代建议一样实现了 OUTPUT 参数。这对我来说是最好的解决方案,因为我在这个阶段不使用用户定义的消息,并且我想避免在这个项目中使用它们。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-15
      • 2016-02-07
      • 2013-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多