2

[Edit - an explanation of the answer is at the foot of the question because the subtlety is not totally obvious from the accepted answer.]

Original question:

I am looking for a quick 'yes this approach should be possible' answer before I commit further effort.

Scenario:

  • I have a simple SQL Server stored procedure that runs a query. I also want to (1) return a varchar(100) parameter declared as OUTPUT, and (2) use the @ReturnValue to return a further (integer) value.
  • I call this procedure from C# using the SqlDatAdapter.DataSet.Fill() approach.
  • Back in the C# code, I can access the procedure parameters but I cannot get the values of either @ReturnValue or the other OUTPUT param.

What am I expecting ?

  • The SqlDataAdapter fill process to grab the data from the select. This works and I can access this data in C#.
  • The values of the SQL Server stored procedure params marked as OUTPUT, and the automatic @ReturnValue param, to be available in C#.

What do I see ?

  • Data is returned from the query as expected.
  • The SQL command parameters are present in C#, but their .Value properties are blank.

What research have I done ?

  • A lot of googling for hits containing SqlDataAdapter and @returnvalue. No significant positive results.
  • Perused the old SO questions related to SqlDataAdapter - nothing seems to overlap my question.
  • Posted this question.

I need to ask the SO community if this is even possible. So - is it possible to collect OUTPUT parameter values from a stored procedure that was used with SqlDatAdapter.DataSet.Fill() ?

If the answer is yes and there are no simple examples out there then I'll cut down my code to a representative case and post it here for (hopefully) community debugging.

EDIT Thanks to @NDJ and the sample code supplied I was able to confirm that SqlDatAdapter.DataSet.Fill() DOES NOT interfere with stored procedure return values. I was then able to go on to discover that my issue was caused by an immediate call to SqlDatAdapter.DataSet.FillScheam(). This delivers the metadata for the data table into a handy matrix that we can access from C#, which is part of my use case.

However, FillSchema() clears the stored procedure parameter values. In my code I was trying to gather the stored procedure output parameter values after the call to FillSchema, which was the cause of what I witnessed. I could not find any documentation on this point.

If your code looks like this, it will work.

    da.Fill(ds);

    var returnVal = returnParam.Value; // value as expected

    da.FillSchema(ds, SchemaType.Source);

If your code looks like this it will not work

    da.Fill(ds);

    da.FillSchema(ds, SchemaType.Source);

    var returnVal = returnParam.Value; // value reset by prev line !!!!
Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67
  • 1
    There is no reason that I am aware of that it won't work. – Crowcoder Jun 26 '20 at 15:48
  • @Crowcoder thanks the that positive ray of light in the tunnel ! Would you happen to have a link to an example ? – Vanquished Wombat Jun 26 '20 at 15:55
  • 1
    I don't have anything handy. But if your stored procedure is setting output(s) and RETURNing a value, then the command should populate the parameters. You also have to be sure you are looking at the right command in the adapter. – Crowcoder Jun 26 '20 at 15:59
  • 1
    A parameter has a direction that defaults to input. You need to set to output : https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlparameter?view=dotnet-plat-ext-3.1 – jdweng Jun 26 '20 at 16:08
  • @jdweng - thank, I believe I already have that in place, particularly because before I set the size of the output param on the C# side I was seeing an exception regarding size=0 being invalid. – Vanquished Wombat Jun 26 '20 at 16:38

1 Answers1

3

yes you can.

Create PROCEDURE TestSP
    @test varchar(max) output
AS
BEGIN
    select @test = 'abc'
    select top 10 * from MyTable
    return 4

END




using (SqlConnection connection = new SqlConnection(conString))
        {
            connection.Open();

            SqlCommand cmd = new SqlCommand("TestSP", connection);
            cmd.CommandType = CommandType.StoredProcedure;

            SqlParameter param = new SqlParameter("@test", SqlDbType.VarChar, -1);
            param.Direction = ParameterDirection.Output;

            var returnParam = cmd.Parameters.Add("@Return", SqlDbType.Int);
            returnParam.Direction = ParameterDirection.ReturnValue;

            cmd.Parameters.Add(param);

            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataSet ds = new DataSet();
            da.Fill(ds);

            var returnVal = returnParam.Value; //4
            var output = param.Value; // abc
            var data = ds.Tables[0].Rows[0][0];  //data from my table
        }
NDJ
  • 5,189
  • 1
  • 18
  • 27
  • I have marked your answer as correct. In my code I immediately call da.FillSchema(ds, SchemaType.Source); after the call to da.fill(). Experimentation shows that the FillSchema() call clears the procedure output variables. Who knew. – – Vanquished Wombat Jun 26 '20 at 17:49
  • 1
    i certainly didn't :) – NDJ Jun 26 '20 at 17:49
  • After pondering this for a while I have intuited as follows: looking at it from the conversation between C# and SQL Svr, the SQL Svr just ran a sproc and returned the results as a standard call to 'Execute' the sproc. Where it gets clever is that C# must then ask SQL Svr to cough up the schema for that previous output. The means to achieve that would likely be a call so some special sproc that SQL Server contains for that purpose. So it seems sensible that given C# just executed a different sproc than my original that the output variables of the first sproc were cleared. – Vanquished Wombat Jul 06 '20 at 15:56