当设置了 ReturnValue 时,SqlCommand 不发送 EXEC @return = Sproc

SqlCommand not sending `EXEC @return = Sproc` when `ReturnValue` is set

我有一个 SQL 服务器存储过程,它 return 有多个结果表,此外还总是 return 一个整数值,例如

CREATE PROCEDURE dbo.Foo
    @someParameter varchar(100) = NULL,
    @anotherParam  date
AS
BEGIN
    IF @anotherParam > GETDATE() BEGIN
        RETURN 1
    END

    SELECT foo, bar FROM someTable    WHERE blargh
    SELECT baz      FROM anotherTable WHERE blargh

    RETURN 0
END

在 C# 中,我这样做:

using( DbCommand cmd = c.CreateCommand() ) {
    // cmd is an instance of SqlCommand despite being cast to DbCommand
    cmd.CommandText = "Foo";
    cmd.CommandType = CommandType.StoredProcedure;
    /* code that adds other parameters here */

    IDataParameter returnParam = cmd.CreateParameter(); // returnParam is an instance of SqlParameter
    returnParam.ParameterName = "@ReturnValue";
    returnParam.Direction     = ParameterDirection.ReturnValue;
    returnParam.DbType        = DbType.Int32;
    cmd.Parameters.Add( paraReturn );

    using( DbDataReader rdr = cmd.ExecuteReader() ) {
        Int32 returnValue = (Int32)returnParam.Value; <-- exception here
    }
}

SQL Server Profiler 显示以下命令已发送到服务器:

exec Foo @someParameter=N'1234ABCD',@anotherParam='2015-01-01 00:00:00'

它没有在任何地方添加 @ReturnValue 参数来捕获存储过程的 return 值。

相比之下,当我使用 SQL Server Management Studio 执行程序时,Profiler 显示这是发送到服务器:

DECLARE @return_value int
EXEC @return_value = Foo @someParameter=N'1234ABCD',@anotherParam='2015-01-01 00:00:00'

为什么?

啊,事实证明存储过程 中的 return 值是 returned:它是 "emitted" 匿名来自 SQL 不需要显式变量来捕获它的服务器(尽管如有必要,您仍然可以捕获它,如我给出的 SQL Server Management Studio 示例)。

因此,虽然 return 值存在,但 DbDataReader 不会填充 ParameterDirection.ReturnValue 参数 ,直到 reader 已关闭。

所以这有效:

IDataParameter returnParam = cmd.CreateParameter(); // returnParam is an instance of SqlParameter
returnParam.ParameterName = "@ReturnValue";
returnParam.Direction     = ParameterDirection.ReturnValue;
returnParam.DbType        = DbType.Int32;
cmd.Parameters.Add( paraReturn );

using( DbDataReader rdr = cmd.ExecuteReader() ) {
    // do stuff
}
Int32 returnValue = (Int32)returnParam.Value;

但这不是:

...
using( DbDataReader rdr = cmd.ExecuteReader() ) {
    Int32 returnValue = (Int32)returnParam.Value;
}