调用带有返回值的存储过程

93

我正在尝试从我的C# Windows应用程序调用存储过程。该存储过程运行在本地安装的SQL Server 2008实例上。我可以调用存储过程,但是无法从存储过程中检索返回的值。该存储过程应该返回序列中的下一个数字。我已经在网上做了研究,所有我看到的网站都指向这个解决方案可行。

存储过程代码:

ALTER procedure [dbo].[usp_GetNewSeqVal]
      @SeqName nvarchar(255)
as
begin
      declare @NewSeqVal int
      set NOCOUNT ON
      update AllSequences
      set @NewSeqVal = CurrVal = CurrVal+Incr
      where SeqName = @SeqName

      if @@rowcount = 0 begin
print 'Sequence does not exist'
            return
      end

      return @NewSeqVal
end

调用存储过程的代码:

SqlConnection conn = new SqlConnection(getConnectionString());
conn.Open();

SqlCommand cmd = new SqlCommand(parameterStatement.getQuery(), conn);
cmd.CommandType = CommandType.StoredProcedure;

SqlParameter param = new SqlParameter();

param = cmd.Parameters.Add("@SeqName", SqlDbType.NVarChar);
param.Direction = ParameterDirection.Input;
param.Value = "SeqName";

SqlDataReader reader = cmd.ExecuteReader();

我还尝试使用 DataSet 获取返回值,但结果相同。我漏掉了什么才能从存储过程中获取返回值?如果需要更多信息,请告诉我。

9个回答

191
你需要向命令添加一个 ReturnValue 方向参数:
using (SqlConnection conn = new SqlConnection(getConnectionString()))
using (SqlCommand cmd = conn.CreateCommand())
{
    cmd.CommandText = parameterStatement.getQuery();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("SeqName", "SeqNameValue");

    // @ReturnVal could be any name
    var returnParameter = cmd.Parameters.Add("@ReturnVal", SqlDbType.Int);
    returnParameter.Direction = ParameterDirection.ReturnValue;

    conn.Open();
    cmd.ExecuteNonQuery();
    var result = returnParameter.Value;
}

将参数的方向设为ParameterDirection.ReturnValue会告诉 SqlCommand 声明它为一个变量并将存储过程的返回值分配给它(exec @ReturnValue = spMyProcedure...),就像你在 SQL 中编写的一样。


3
你需要在存储过程中添加 @ReturnVal 吗? - muttley91
4
不需要添加到sp(返回语句会触发填充标记为ReturnValue的参数)。 - ufosnowcat
15
无论你把返回值参数命名为什么,都没有关系。 - Clay
非常感谢,我一直在寻找一个正确获取返回值的方法,这个方法对我真的很有用。太棒了! - Oswaldo Zapata
5
存储过程的返回值应该仅用于指示成功(零)或失败/警告(非零),而不是返回数据。请使用OUTPUT参数或结果集来实现这个目的。 - Dan Guzman
显示剩余2条评论

7

我知道这是老旧的内容,但我通过谷歌偶然发现了它。

如果你在存储过程中有一个返回值,建议使用"Return 1",而不是使用输出参数。

可以采用以下方式 - "@RETURN_VALUE"会被自动添加到每个命令对象中,无需显式添加。

    cmd.ExecuteNonQuery();
    rtn = (int)cmd.Parameters["@RETURN_VALUE"].Value;

2
嗨,Gary。你有引用文献吗? - Michael
5
我认为,除非显式添加,否则cmd实例的SqlParameterCollection中不会有名为RETURN_VALUE的参数。因此,如果您考虑采用Gary上面的方法,请确保已添加cmd.Parameters.Add(new SqlParameter("@RETURN_VALUE", SqlDbType.Int)); cmd.Parameters["@RETURN_VALUE"].Direction = ParameterDirection.ReturnValue; 但是,我认为您不必将@RETURN_VALUE添加到存储过程定义中。也许这就是Gary的意思。 - Judy007
我有一些源代码,它运行SqlCommandBuilder.DeriveParameters,它似乎会自动将其第一个参数创建为@RETURN_VALUE(除非我错过了其他导致这种情况发生的因素)。DeriveParameters比手动创建参数要慢。我只使用它一次并缓存结果。 - Mafu Josh

5
我机器上的EnterpriseLibrary版本有其他参数。 这个是可行的:
        SqlParameter retval = new SqlParameter("@ReturnValue", System.Data.SqlDbType.Int);
        retval.Direction = System.Data.ParameterDirection.ReturnValue;
        cmd.Parameters.Add(retval);
        db.ExecuteNonQuery(cmd);
        object o = cmd.Parameters["@ReturnValue"].Value;

3

ExecuteScalar();可以工作,但输出参数将是更优秀的解决方案。


8
除非你选择返回值,否则ExecuteScalar()无法正常工作。 - Soori

3

这是一个从过程中返回单个值的非常简短的示例:

SQL:

CREATE PROCEDURE [dbo].[MakeDouble] @InpVal int AS BEGIN 
SELECT @InpVal * 2; RETURN 0; 
END

C#代码:

int inpVal = 11;
string retVal = "?";
using (var sqlCon = new SqlConnection(
    "Data Source = . ; Initial Catalog = SampleDb; Integrated Security = True;"))
{
    sqlCon.Open();
    retVal = new SqlCommand("Exec dbo.MakeDouble " + inpVal + ";", 
        sqlCon).ExecuteScalar().ToString();
    sqlCon.Close();
}
Debug.Print(inpVal + " * 2 = " + retVal); 
//> 11 * 2 = 22

3

我遇到了与SP调用相关的问题,返回错误信息说缺少某个参数。我的代码如下:

@Result int OUTPUT

同时,以下是C#代码:

            SqlParameter result = cmd.Parameters.Add(new SqlParameter("@Result", DbType.Int32));
            result.Direction = ParameterDirection.ReturnValue;
在故障排除中,我意识到存储过程实际上是在寻找“InputOutput”方向,所以以下更改解决了问题。
            r

结果.方向 = 参数方向.输入输出;

2
“输出参数”和存储过程的返回值不是同一回事,顺便说一下。 - Dai

2

1

如果您使用的是EnterpriseLibrary而不是标准的ADO.NET...

Database db = DatabaseFactory.CreateDatabase();
using (DbCommand cmd = db.GetStoredProcCommand("usp_GetNewSeqVal"))
{
    db.AddInParameter(cmd, "SeqName", DbType.String, "SeqNameValue");
    db.AddParameter(cmd, "RetVal", DbType.Int32, ParameterDirection.ReturnValue, null, DataRowVersion.Default, null);

    db.ExecuteNonQuery(cmd);

    var result = (int)cmd.Parameters["RetVal"].Value;
}

-10

我看到另一个已经关闭了。所以这里基本上是我的代码草稿。我认为你缺少了字符串cmd的注释。例如,如果我的存储过程被称为:DBO.Test。那么我需要写cmd="DBO.test"。然后执行命令类型等于存储过程,等等。

Connection.open();
String cmd="DBO.test"; //the command
Sqlcommand mycommand;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接