【问题标题】:How can retrieve both a result set and a PRINT message from a SQL stored procedure in C#?如何从 C# 中的 SQL 存储过程中检索结果集和 PRINT 消息?
【发布时间】:2015-08-22 09:53:58
【问题描述】:

我有一个存储过程,它可以返回 PRINT 消息和来自 SELECT 语句的常规查询结果。使用this thread 中提供的优雅解决方案,我可以在我的C# 代码中调用SqlCommand.ExecuteNonQuery() 时轻松捕获我的PRINT 消息。问题是,我也想返回我的结果集。

当我使用SqlDataReader 使用SqlCommand.ExecuteReader 从我的存储过程中取回查询结果时,捕获我的PRINT 消息的事件处理程序永远不会像我使用SqlCommand.ExecuteNonquery 时那样触发。

开始似乎我只能拥有一个或另一个。有没有办法让我同时捕获我的PRINT 消息我的查询结果?

这是我的 SQL 存储过程的 sn-p:

IF EXISTS
(
    SELECT MFG_PART_NUMBER, COUNT(*) AS RecordCount
    FROM [PartsManagement].[ManufacturerCatalogPart] 
    WHERE CatalogID = @CatalogID
    GROUP BY MFG_PART_NUMBER
    HAVING COUNT(*) > 1
)
    BEGIN

    SELECT MFG_PART_NUMBER, COUNT(*) AS RecordCount
    FROM [PartsManagement].[ManufacturerCatalogPart] 
    WHERE CatalogID = @CatalogID
    GROUP BY MFG_PART_NUMBER
    HAVING COUNT(*) > 1;

    PRINT 'The update was not executed because duplicate MFG_PART_NUMBERs were found in the catalog. Delete the duplicates and try again.';

END;

这是我的代码中的一个 sn-p:

//field
private string sqlPrintMessage = "";

//event handler for sql PRINT message
void myConnection_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
    sqlPrintMessage = e.Message;
}

public void SomeMethod()
{
    using (SqlConnection sqlcon = new SqlConnection(ConnectionManager.GetConnectionString()))
    {
        sqlcon.InfoMessage += new SqlInfoMessageEventHandler(myConnection_InfoMessage);

        SqlCommand sqlcmd = new SqlCommand("[ManufacturerCatalogUpdate].[CheckCatalogForDuplicates]", sqlcon);
        sqlcmd.CommandType = CommandType.StoredProcedure;
        sqlcmd.Parameters.AddWithValue("@CatalogID", catalogID);
        sqlcon.Open();
        //SqlDataReader reader = sqlcmd.ExecuteReader();
        sqlcmd.ExecuteNonQuery();
        if (sqlPrintMessage == "") //then proceed with update
        {
            //do some logic
        }
        else
        {
            //System.Diagnostics.Debug.WriteLine(sqlPrintMessage);
            //List<DuplicatePartCount> duplicateParts = new List<DuplicatePartCount>();
            //while (reader.Read())
            //{
            //    DuplicatePartCount record = new DuplicatePartCount();
            //    record.MFG_PART_NUMBER = reader.GetString(0);
            //    record.count = reader.GetInt32(1);
            //    duplicateParts.Add(record);
            //}
        }
    }
}

【问题讨论】:

  • 为连接 InfoMessage 事件添加一个处理程序,以获取由 PRINTRAISERROR 生成的信息和警告消息(严重性为 10 或更低)。
  • @DanGuzman,我已经在这样做了(请参阅我的第二个代码块)。问题是,当我使用ExecuteReader 时,该事件永远不会被触发。
  • Jacob,这里发生了其他事情。我确实看到了事件处理程序,但我可以向您保证,这应该可以工作,因为我已经多次这样做了。当然,这可能是时间问题。我知道,例如,在您关闭阅读器之前,输出参数值不可用。稍后我会看一下并与我的工作进行比较。如果之前没有其他人有答案,那么我会尝试找到问题。
  • 您需要在 InfoMessage 事件处理程序触发之前导航到阅读器结果的末尾。在底层的 TDS 流协议中,InfoMessage 位于 select 语句的上述结果后面,并且在被消费之前不可用。
  • Jacob:不,这与 DataTables 和/或 DataAdapter 无关。如果有的话,使用带有 Fill 方法的 DataTable 的人可能会更快地收到消息,但这只是因为 fill 方法在继续执行下一行代码之前完全通过阅读器。最后,您不需要我在回答中显示的消息。你只需要reader.HasRows()

标签: c# sql-server stored-procedures resultset


【解决方案1】:

根据选择的sql执行有两种方法。

1) 如果您使用的是ExecuteNonQuery,而不是像下面这样在存储过程中使用OUT 参数。

CREATE PROCEDURE GetEmployee
   @employeeID INT,
   @Message VarChar(100) OUTPUT
AS
BEGIN
   -- Here is your result set from select statement
   SELECT Emp_Name From Employee Where EmpId = @employeeID 

   -- Here is your message
   SELECT @Message = 'Your Message!'
END

现在要在您的代码中使用@Message 参数和OUT 来获取它,如下所示。

//Add the output parameter to the command object
SqlParameter outPutParameter = new SqlParameter();
outPutParameter.ParameterName = “@Message”;
outPutParameter.SqlDbType = System.Data.SqlDbType.Varchar;
outPutParameter.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(outPutParameter);

cmd.ExecuteNonQuery();

//Retrieve the value of the output parameter
string Message = outPutParameter.Value.ToString();

// You can get remaining result set as always

2) 如果您使用的是 Reader,则...

设置消息并使用 Select 语句而不是 print.Like below'

 SELECT Emp_Name From Employee Where EmpId = @employeeID 
  -- It should be set previously
  SELECT @Message

要进入您的代码,只需使用

using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                //Your result set or message
            }

            if(reader.NextResult())
            {
               while (reader.Read())
              {
                 //Your result set or message
              }
            }
        }

【讨论】:

  • 这既复杂又不必要。请(每个人)阅读问题中 OP 发布的代码,而不仅仅是问题的标题。虽然 OP 询问是否捕获消息,但这并不是 OP 最终真正想要完成的任务。如果捕获消息确实是这里的目标,那么这仍然无济于事,OUTPUT 参数是一条消息,并且额外的结果集很笨重,并且会在各种情况下对 proc 的运行产生不利影响,例如尝试将结果集捕获到临时表等中。
  • @srutzky:请告诉我们您的理解……并提供更好的解决方案。
  • 此外,使用 ExecuteNonQuery 的场景已经由 OP 正确处理,并在问题中声明为按需要工作。
  • @srutzky:这只是代码示例。我从来没有提到它可以在任何地方使用。
  • 我已经按照您在此问题的回答中提出的要求完成了。重申一下:OP 只是首先寻找存在结果集的指示,甚至从不使用消息的文本。这是一种糟糕的测试方法,因为 SqlDataReader 通过布尔值 HasRows 属性提供了答案。
【解决方案2】:

所以,我认为我们首先需要重新考虑这里的操作。您想要进行复制作为测试以确定应用程序代码是否应该继续。 proc 返回一个结果集,如果有欺骗,则打印一条消息,否则都不返回。而且您推断将有一个基于捕获消息的存在的结果集。这种类型的间接测试不太理想且没有必要。

可以使用一种直接且更简单的方法来测试结果集是否存在。忘记该消息(尤其是因为您甚至没有使用它),而只需检查数据阅读器:

    if (reader.HasRows())
    {
        //System.Diagnostics.Debug.WriteLine(sqlPrintMessage);
        //List<DuplicatePartCount> duplicateParts = new List<DuplicatePartCount>();
        //while (reader.Read())
        //{
        //    DuplicatePartCount record = new DuplicatePartCount();
        //    record.MFG_PART_NUMBER = reader.GetString(0);
        //    record.count = reader.GetInt32(1);
        //    duplicateParts.Add(record);
        //}
    }
    else
    {
        //do some logic
    }

如果您仍然只是对 PRINT 消息被困感到好奇,请进行以下两个测试:

  1. 首先,在while 循环之后添加第二个System.Diagnostics.Debug.WriteLine(sqlPrintMessage);,然后重新运行测试
  2. 其次,在不删除#1 中的第二个调试行的情况下,在BEGINSELECT 之间添加第二个PRINT 命令,使用不同的文本,以便在必要时区分它们,然后重新运行测试

此外,来自 MSDN 页面的 SqlConnection.FireInfoMessageEventOnUserErrors 属性的以下“备注”可能会阐明此行为:

当您将 FireInfoMessageEventOnUserErrors 设置为 true 时,以前被视为异常的错误现在将作为 InfoMessage 事件处理。所有事件立即触发并由事件处理程序处理。如果 FireInfoMessageEventOnUserErrors 设置为 false,则 InfoMessage 事件将在过程结束时处理。

因此,如果您想要/需要消息发生时,那么您可以将FireInfoMessageEventOnUserErrors 设置为true,但这可能会改变捕获异常的行为,这些异常可能更不受欢迎立即收到消息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多