从存储过程中检索 return 值

Retrieve return value from stored procedure

我写了一个 table 值参数存储过程,它 return 是插入的 Id。如果我从 SSMS 内部的查询中 运行 它,它会工作并且 return 值正确。

当我从代码调用该过程时,它 运行s,并插入数据但没有设置我添加并标记为 return 参数的 return 参数。

我做错了什么?

SP:

ALTER PROCEDURE [dbo].[InsertUpdateMeeting] 
    -- Add the parameters for the stored procedure here
    @Meeting MeetingsTableType READONLY,
    @Area AreasTableType READONLY,
    @MeetingLocation MeetingLocationsTableType READONLY

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    -- Insert statements for procedure here
    DECLARE @MeetingId int = 0;
    DECLARE @AreaId int;
    DECLARE @MeetingLocationId int;

    SELECT TOP 1 @MeetingId=m.MeetingId FROM @Meeting AS m;
    SELECT TOP 1 @MeetingLocationId = ml.MeetingLocationId FROM @MeetingLocation AS ml;
    SELECT TOP 1 @AreaId = a.AreaId FROM  @Area AS a;

    UPDATE Areas SET 
        Hotline=areaParam.Hotline,
        Name=areaParam.Name
    FROM Areas INNER JOIN @Area AS areaParam
    ON Areas.AreaId=areaParam.AreaId;
    IF(@@ROWCOUNT=0)
        BEGIN;
            INSERT INTO Areas
                (Hotline,Name)
            SELECT areaParam.Hotline,areaParam.Name FROM @Area AS areaParam;
            SET @AreaId=SCOPE_IDENTITY();
        END;
    UPDATE MeetingLocations SET
        Address1=ml.Address1,
        Address2=ml.Address2,
        Address3=ml.Address3,
        City=ml.City,
        [State]=ml.[State],
        Zip=ml.Zip,
        ClubName=ml.ClubName,
        ClubDescription=ml.ClubDescription,
        MapLink=ml.MapLink
    FROM MeetingLocations INNER JOIN @MeetingLocation AS ml
    ON MeetingLocations.MeetingLocationId=ml.MeetingLocationId;
    IF(@@ROWCOUNT=0)
        BEGIN;
            INSERT INTO MeetingLocations 
                (Address1,Address2,Address3,City,[State],Zip,ClubName,ClubDescription,MapLink)
            SELECT ml.Address1,ml.Address2,ml.Address3,ml.City,ml.[State],ml.Zip,ml.ClubName,ml.ClubDescription,ml.MapLink
            FROM @MeetingLocation AS ml;
            SET @MeetingLocationId=SCOPE_IDENTITY();
        END;
    UPDATE Meetings SET 
        AreaId = @AreaId,
        MeetingLocationId=@MeetingLocationId,
        Name = meetingParam.Name
    FROM Meetings INNER JOIN @Meeting AS meetingParam
    ON Meetings.MeetingId=meetingParam.MeetingId;
    IF(@@ROWCOUNT=0)
        BEGIN;
            INSERT INTO Meetings
                (MeetingLocationId,AreaId,Name)
            SELECT @MeetingLocationId,@AreaId,meetingParam.Name FROM @Meeting AS meetingParam;
            SET @MeetingId=SCOPE_IDENTITY();
        END;
    RETURN @MeetingId;
END

C#代码:

...create tables...
var meetingLocationParam = new SqlParameter("@MeetingLocation", meetingLocationTable);
meetingLocationParam.SqlDbType = SqlDbType.Structured;
meetingLocationParam.TypeName = "MeetingLocationsTableType";
var meetingParam = new SqlParameter("@Meeting", meetingTable);
meetingParam.SqlDbType = SqlDbType.Structured;
meetingParam.TypeName = "MeetingsTableType";
var areaParam = new SqlParameter("@Area", areaTable);
areaParam.TypeName = "AreasTableType";
areaParam.SqlDbType = SqlDbType.Structured;
using (var connection = new SqlConnection(conn.ConnectionString))
using (var command = new SqlCommand())
{
    connection.Open();
    command.Connection = connection;
    command.CommandText = "EXEC dbo.InsertUpdateMeeting";
    command.Parameters.AddRange(new SqlParameter[]
    {meetingParam, areaParam, meetingLocationParam, new SqlParameter()
    {
        Value=0,
        Direction=ParameterDirection.ReturnValue,
        SqlDbType=SqlDbType.Int,
        ParameterName="@MeetingId"
    } });
    var otherRet = command.ExecuteNonQuery();

    //Assert.IsTrue(ret > 0);
}

您需要使用ExecuteScalar来捕获返回值,或者使用output参数。

此 post 中回答了类似的问题。 Calling stored procedure with return value

该输出值不会从 ExecuteNonQuery 得到 returned。尝试像这样创建对 return 参数的引用:

var returnParameter = new SqlParameter[]
{meetingParam, areaParam, meetingLocationParam, new SqlParameter()
{
    Value=0,
    Direction=ParameterDirection.ReturnValue,
    SqlDbType=SqlDbType.Int,
    ParameterName="@MeetingId"
} }

command.Parameters.Add(returnParameter);

然后执行后,

var myReturnedValue = returnParameter.Value;

您也可以更改

RETURN @MeetingId;

SELECT @MeetingId;

然后使用

var myReturnedValue = (int)command.ExecuteScalar();

有多种方法可以使用 ADO.NET 从存储过程中获取 return 值。

我的示例是我以前使用过的通用方法。您可以使用存储过程或文本传递 SQL 命令。

  1. SqlAdapter

    /// <summary>
    /// Returns a DataSet given an Sql Command
    /// </summary>
    /// <param name="cmd">The SqlCommand object</param>
    /// <returns>DataSet</returns>
    public static DataSet GetDataSet(SqlCommand cmd)
    {
        using (SqlConnection con = DbConnect()) // DbConnect() returns SqlConnection instance
        {
            DataSet ds = new DataSet();
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            cmd.Connection = con;
            da.Fill(ds, "my_dataset");
    
            return ds;
        }
    }
    
  2. 执行标量

    /// <summary>
    /// Returns a single value from the database given an Sql Command object
    /// </summary>
    /// <param name="cmd">The Sql Command object</param>
    /// <returns>string</returns>
    public static string GetSingleFromDatabase(SqlCommand cmd)
    {
        using (SqlConnection con = DbConnect()) // DbConnect() returns SqlConnection instance
        {
            cmd.Connection = con;
    
            try
            {
                object returnedValue = cmd.ExecuteScalar();
                if (returnedValue != null)
                {
                    return returnedValue.ToString();
                }
                else
                    return "";
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
    

我刚刚注意到您没有在 SP 定义中声明您的 @MeetingId 参数(您在正文中声明了,但我认为不能使用)。

ALTER PROCEDURE [dbo].[InsertUpdateMeeting] 
    -- Add the parameters for the stored procedure here
    @Meeting MeetingsTableType READONLY,
    @Area AreasTableType READONLY,
    @MeetingLocation MeetingLocationsTableType READONLY,
    @MeetingId <datatype> OUTPUT -- ADD THIS
AS

然后在你的代码中你可以用这个来获取它:

command.Parameters["@MeetingId"].Value