【问题标题】:ASP.NET SQL Server stored procedure return MessageASP.NET SQL Server 存储过程返回消息
【发布时间】:2017-07-21 09:47:03
【问题描述】:

我有一个不需要任何参数的存储过程,它返回值 0 和消息:

(94 row(s) affected)

(1 row(s) affected)

我的问题是如何获取消息:

(94 row(s) affected)

(1 row(s) affected)

这是我调用存储过程的 .NET 方法:

public List<MessageClass> ChequesToUpdate()
{
    message = new List<MessageClass>();

    MessageClass item = new MessageClass();

    try
    {
        using (connection = new SqlConnection(connectionString))
        {
            connection.Open();

            using (SqlCommand command = new SqlCommand("MyStoredProcedure", connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                command.ExecuteNonQuery();

                item.message = "message";
            }
        }
    }
    catch (Exception e)
    {
        item.message = e.Message;
    }
    finally
    {
        connection.Close();
    }

    message.Add(item);

    return message;
}

我希望将消息放入item.message,我该如何完成?

【问题讨论】:

  • 这通常不是存储过程的工作方式:您应该尝试将业务逻辑限制在您的 C# 代码中。 sproc 应该做它需要做的任何事情,你可以根据返回的标量值或表值来决定生成什么样的消息。话虽如此,您可以在存储过程中执行类似select 'it worked' as message 的操作,并在代码中查询message 列。
  • 您所说的消息是由sql management studio等执行程序生成的。为什么不使用command.executeNonquery()的返回值,如var recordsAffected=command.executenonquery()并设置消息=$"{recordsAffected} 条记录受影响"
  • 仅供参考:SqlConnection.InfoMessage 在 T-SQL 中与 print 一起工作就像一个魅力,但不幸的是,这些消息没有。
  • 您希望返回受命令影响的行数并将其保存到 int 变量中,但由于语句类型为 select,因此它返回 -1。阅读这篇文章了解原因:stackoverflow.com/a/38060528/2946329

标签: c# asp.net sql-server stored-procedures


【解决方案1】:

ExecuteNonQuery 返回受影响行的总数。因此,如果您只想要总行数,那么您可以使用以下语句获取它:

var x = command.ExecuteNonQuery();

否则,您必须在存储过程中使用用户定义RAISERROR 消息并从C# connection.InfoMessage 事件中捕获它。我已经设置了一个测试环境并对其进行了测试。我创建了一个表并插入了一些数据来检查我的 SQL 和 C# 代码。请检查下面的 SQL 和 C# 代码。

SQL:

Create Table psl_table
(
    [values] NVarChar(MAX)
)

Insert Into psl_table Values('a')
Insert Into psl_table Values('a')
Insert Into psl_table Values('a')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')
Insert Into psl_table Values('b')

Create Proc MyStoredProcedure
    As
Begin
    -- Declare a variable for Message
    Declare @Msg NVarChar(MAX)

    -- 1st SQL Statement
    Update psl_table Set [Values]='a' Where [Values]!='a'

    -- Generate the message and print that can get from C#
    Set @Msg = '(' + Convert(NVarChar,@@RowCount) + ' row(s) affected)'
    RAISERROR( @Msg, 0, 1 ) WITH NOWAIT

    -- 2nd SQL Statement
    Update psl_table Set [Values]='a'

    -- Generate the message and print that can get from C#
    Set @Msg = '(' + Convert(NVarChar,@@RowCount) + ' row(s) affected)'
    RAISERROR( @Msg, 0, 1 ) WITH NOWAIT
End

在这个 SQL 中,我声明了一个变量 @Msg 来存储消息和内置函数 RAISERROR 来抛出消息。

C#代码:

public List<MessageClass> ChequesToUpdate()
{
    message = new List<MessageClass>();

    MessageClass item = new MessageClass();

    try
    {
        using (connection = new SqlConnection(connectionString))
        {
            connection.Open();

            connection.InfoMessage += delegate (object sender, SqlInfoMessageEventArgs e)
            {
                item.message = e.Message;
            };

            using (SqlCommand command = new SqlCommand("MyStoredProcedure", connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                command.ExecuteNonQuery();
            }
        }
    }
    catch (Exception e)
    {
        item.message = e.Message;
    }
    finally
    {
        connection.Close();
    }

    message.Add(item);

    return message;
}

我已修改您的代码以获得所需的输出。我使用 connection.InfoMessage 事件来捕获从 SQL 抛出的消息。

出于测试目的,我在控制台中打印了输出。

输出:

【讨论】:

【解决方案2】:

您无法捕获这些特定消息(即(94 row(s) affected)),因为它们不是由 SQL Server 发送的;它们由客户端程序(SQLCMD 或 SSMS)发送。但是,是的,您可以捕获行数,而无需在存储过程中添加 RAISERRORPRINT 语句。此外,您(或许多阅读此问题的其他人)可能甚至无法更新存储过程以注入额外的 PRINT / RAISERROR / SELECT 语句来输出行数。

但首先,关于此处提出的其他一些建议的两个警告:

  1. 您可能无法使用ExecuteNonQuery 的返回值,因为它不返回非DML 语句的值,包括SELECT 语句。它只返回来自INSERTUPDATEDELETE 语句的值,如此处所述SqlCommand.ExecuteNonQuery

    对于 UPDATE、INSERT 和 DELETE 语句,返回值是受命令影响的行数。 ...对于所有其他类型的语句,返回值为-1。

    该问题没有说明正在执行哪些类型的查询,因此您可能只使用这些 DML 语句。不过,每个人都应该知道这个返回值反映了什么(什么没有反映)。

  2. 您可能不想通过 InfoMessage 处理程序捕获打印消息,因为这需要解析以确保捕获的消息不是其他消息,例如警告或其他信息。

话虽如此,您想设置一个SqlCommand.StatementCompleted 事件处理程序,因为它会传递每个语句受影响的行(RecordCount 是一个int)。

internal static void StatementCompletedHandler(object Sender,
StatementCompletedEventArgs EventInfo)
{
    // do something with EventInfo.RecordCount

    return;
}

然后将其附加到SqlCommand 对象:

_Command.StatementCompleted += StatementCompletedHandler;

如果在 ASP.NET 中工作限制了您将返回值存储在静态变量中的能力(因为它是共享的并且可能不是线程安全的),那么您可以将处理程序内联定义为匿名委托,在这种情况下它将能够访问在您的 ChequesToUpdate 方法中声明的实例变量。

【讨论】:

    【解决方案3】:

    我会从过程中返回@@rowcount,然后在 .net 代码中我会读取该值。

    在您的过程中添加返回(@@rowcount),如下所示

    create procedure testprocedure
    as
    begin
        update test1 set id =100
        return(@@rowcount)
    end
    

    您的 .net 代码如下所示,带有参数方向

        SqlConnection conn;
            using(conn = new SqlConnection(@"Data Source=;Initial Catalog=;User ID = sa;Password="))
            {
                conn.Open();
    
                using (SqlCommand command = new SqlCommand("testprocedure", conn))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    SqlParameter retValue = command.Parameters.Add("return", SqlDbType.Int);
                    retValue.Direction = ParameterDirection.ReturnValue;
                    command.ExecuteNonQuery();
                    Console.WriteLine("no of records affected " + retValue.Value);
                    Console.ReadLine();
                }
            } 
    

    【讨论】:

      【解决方案4】:

      默认情况下,您将无法在C# 中获取受影响的消息行。相反,您需要将您的过程修改为PRINT/RAISERROR 消息,这样您就可以使用@Julian 提到的SqlConnection.InfoMessage 在C# 中访问它们。

      Return rows affected from a Stored Procedure on each INSERT to display in ASP.NET page - Stackoverflow

      【讨论】:

        【解决方案5】:

        存储过程中语句的每一端,在表中插入@@rowcount。然后使用数据集将其取回您的方法。

        【讨论】:

        • 这不是和我在一周前的回答中提到的完全一样吗?
        【解决方案6】:

        在您的存储过程中,您可以查询@@ROWCOUNT,这将为您提供受影响的记录。现在您可以使用 SETSELECT 语句将其存储到变量中,例如

        SET MyRecordCount = @@RowCount
        

        SELECT MyRecordCount = @@RowCount
        

        或者,如果您在单个过程中有多个操作需要跟踪,您可以创建多个变量并多次调用SETSELECT,或者使用TABLE 变量,例如。

        DECLARE @recordCount table (Records int not null)
        
        --RUN PROCEDURE CODE
        
        INSERT INTO @recordCount VALUES (@@ROWCOUNT)
        
        --RUN MORE PROCEDURECT CODE
        
        INSERT INTO @recordCount VALUES (@@ROWCOUNT)
        
        --RETURN THE Row Count
        SELECT Records FROM @recordCount
        

        这会将@@ROWCOUNT 的值插入到表变量@recordCount

        接下来要获取此信息,您需要调用@recordCount 表中的最后一行选择。

        最后,在您的代码中,您应该使用数据阅读器,而不是使用 ExecuteNonQuery() 方法。

        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
        
            using (SqlCommand command = new SqlCommand("MyStoredProcedure", connection))
            {
                command.CommandType = CommandType.StoredProcedure;
        
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        item.message = reader.GetString(0);
                    }
                    reader.Close();
                }
            }
        
        }
        

        现在消息实际上是受影响行的整数,而不是术语 (98) row affected,但如果您真的想要确切的消息,可以按照您的意愿格式化字符串。

        item.message = string.Format("({0}) rows affected", reader.GetInt32(0))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-23
          • 1970-01-01
          • 2016-11-03
          • 2014-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多