Showing posts with label SQL 2005. Show all posts
Showing posts with label SQL 2005. Show all posts

Wednesday, 15 October 2008

Type of identity returned from SQL Stored Proc

Consider a SQL 2005 Stored Proc which is invoked from C# .net code via a SqlCommand, the stored proc inserts a row and returns the new identity, which is typed as bigint with a statement similar to:

SELECT Scope_Identity()

In the C# code the identity can be read from a call to the SqlCommand ExecuteScalar() method assume the identity in this example is a SQL bigint (which maps to a long / Int64 in dot net).

long myNewId = (long)cmd.ExecuteScalar();
// vb equiv for the cast to long is DirectCast()

(If invoked in the immediate window you’d see : “Cannot unbox 'obj' as a 'long'” )
What's going on?


The problem seems to be that whatever the type of your identity the object returned by ExecuteScalar() is in fact a Decimal, changing the code slightly to:

Object obj = cmd.ExecuteScalar();

Followed by this in debug, immediate window

? obj is System.Decimal

... Will yield "true", so the call is returning a "boxed" decimal, not a long at all… A quick look in the SQL2005-docs online and the return type is simply listed as "numeric".

After some fishing on Google, the consensus across a few posts (one example) seems to be that MS chose to use the largest type possible for the value returned by Scope_Identity() and @@IDENTITY because an identity column could be of various types, smallint, int, bigint, decimal and it made sense to make the return value the largest.

The problem - at least for the unwary developer - is that casting in the usual way (long)cmd.ExecuteScalar() won’t work, as this cast works in a similar way to the VB DirectCast() which doesn’t perform a check for fundamental types. One could argue you should always check the type before trying to unbox.

Note: VB CType() would allow unboxing of any fundamental type, to any other fundamental type, System.Convert() would also work in C#. See
this good article - with examples in VB - for more info on boxing.

In the end I worked around this by defining a variable of the correct type in the stored proc and assigning the identity value to that and passing the variable back in the stored proc "select" statement.