Search code examples
c#stored-proceduressql-server-2012table-valued-parameters

Retrieve return value from stored procedure


I have written a table-valued parameter stored procedure, It returns the Id that was inserted. If I run it from a query inside SSMS, it works and returns the value correctly.

When I call the procedure from code, it runs, and inserts the data but does not set the return parameter I have added and marked as a return parameter.

What am I doing wrong here?

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# code:

...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);
}

Solution

  • That output value won't get returned from ExecuteNonQuery. Try creating a reference to your return parameter like this:

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

    Then after executing,

    var myReturnedValue = returnParameter.Value;
    

    You could also change

    RETURN @MeetingId;
    

    to

    SELECT @MeetingId;
    

    and then use

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