Question

Can anyone tell me what is going on in this function?? In the following code snippet, user.Id = 0, id.Value = 0 and id.SqlDbType = Int.. as expected since user.Id is an int field.

However, error.Value = null and error.SqlDbType = BigInt. What gives? If I use non-zero it detects an int and the correct value.

Note: the Value properties are the same before and after declaring the parameter direction.

public static long InsertUpdate(User user) {

    SqlParameter id = new SqlParameter("@id", user.Id);
    id.Direction = ParameterDirection.InputOutput;
    cmd.Parameters.Add(id);

    SqlParameter error = new SqlParameter("@error_code", 0);
    error.Direction = ParameterDirection.Output;
    cmd.Parameters.Add(error);

    .... other stuff
}

As well, if @SET @error_Code = 0 in the sproc, error.Value = NULL and error.SqlDbType = NVarChar AFTER the procedure runs. If I set it to an integer I get an Int type.

UPDATE: After specifying SqlDbType.Int the parameter now has the correct SqlDbType before and after the command... however the stored procedure is still setting @error_code = null when I in fact set it to 0.

UPDATE: When the sproc executes the SELECT statement the @error_code parameter is always returned as null, regardless of when or not it has been set... this only happens when there's a select statement...

Here is the procedure to reproduce:

ALTER PROCEDURE [dbo].[usp_user_insert_v1]
    @username VARCHAR(255),
    @password VARCHAR(255),
    @gender CHAR(1),
    @birthday DATETIME,
    @error_code INT OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @default_dt DATETIME
    EXEC @default_dt = uf_get_default_date
    DECLARE @dt DATETIME = GETUTCDATE()

            INSERT INTO users(username, password, gender, birthday, create_dt, last_login_dt, update_dt, deleted)
            VALUES(@username, @password, @gender, @birthday, @dt, @default_dt, @default_dt, 0)
            SELECT * FROM users WHERE id = SCOPE_IDENTITY()
            SET @error_code = 3
            RETURN

END

SOLUTION?

http://forums.asp.net/t/1208409.aspx?Interesting+problem+with+getting+OUTPUT+parameters+from+SQL+Server+using+C+

Found this link on the ASP forums... apparently you can't read the output parameter until you have read all the results from the SqlDataReader... very unfortunate for me since I decide whether or not I even WANT to read the results based on the output param...

Was it helpful?

Solution 2

Both of the current answers are slightly incorrect because they're based on the assumption that the constructor being called for your error object is the (string,object) one. This is not the case. A literal 0 can be converted to any enum type1, and such a conversion would be preferred over a conversion to object. So the constructor being called is the (string,SqlDbType) constructor.

So the type is set to BigInt because that's the 0 value for the SqlDbType enumeration, and the Value is null because you have no code that attempts to set the value.

SqlParameter error = new SqlParameter("@error_code", (object)0);

should cause it to select the correct overload.


Demo:

using System;
using System.Data;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main()
        {
            var a = new ABC("ignore", 0);
            var b = new ABC("ignore", (object)0);
            var c = new ABC("ignore", 1);
            int i = 0;
            var d = new ABC("ignore", i);
            Console.ReadLine();
        }

    }

    public class ABC
    {
        public ABC(string ignore, object value)
        {
            Console.WriteLine("Object");
        }

        public ABC(string ignore, SqlDbType value)
        {
            Console.WriteLine("SqlDbType");
        }
    }
}

Prints:

SqlDbType
Object
Object
Object

1From the C# Language specification, version 5, section 1.10 (that is, just in the introduction to the language, not buried deep down in the language lawyery bits):

In order for the default value of an enum type to be easily available, the literal 0 implicitly converts to any enum type. Thus, the following is permitted.

Color c = 0;

I'd have also thought this important enough to be in the Language Reference on MSDN but haven't found a definitive source yet.

OTHER TIPS

From SqlParameter.Value on MSDN

For output and return value parameters, the value is set on completion of the SqlCommand

i.e. I wouldn't rely on type inference to set the return type implicitly. I would explicitly set the type of the output parameter:

var error = new SqlParameter("@error_code", SqlDbType.Int)
{
    Direction = ParameterDirection.Output
};

Edit

After some reflection of SqlParameter:

The BigInt is easy to explain - it is the default SqlDbType, and the SqlParameter(string parameterName, object value) ctor doesn't overwrite this value.

public enum SqlDbType
{
  BigInt = 0,
  ...

Re: @error_code is returned as NULL

The only thing I can think of is that the PROC fails to complete cleanly. Try moving the SET @error_code = 0 above the EXEC @default_dt = uf_get_default_date ?

Edit

Confirmed, @Damien's point is correct

SqlParameter error = new SqlParameter("@error_code", 0);

Actually calls this ctor:

public SqlParameter(string parameterName, SqlDbType dbType)

whereas

SqlParameter error = new SqlParameter("@error_code", 1234);

calls

public SqlParameter(string parameterName, object value)

Reason : 0 is implicitly castable to enum.

Well, it looks like the most reliable way of doing it is by using this overload:

SqlParameter error = new SqlParameter("@error_code", SqlDBType.Int);
error.Value = 0;

The overload you're using takes an object as a parameter, and for some reason which I can't divine, it's not picking the right type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top