views:

4781

answers:

7

I got the above error in my app. Here is the original code

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }


I replaced with

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

Is there a better way around this?

+2  A: 

A shorter form can be used:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.TosString ()

EDIT: Haven't paid attention to ExecuteScalar. It does really return null if the field is absent in return result.

So use instead:

return (accountNumber == null) ? string.Empty : accountNumber.TosString ()
User
That won't work - the "accountNumber" is *not* a database value but a regular old Plain Old .NET "object" instance - you need to check against normal "null" value. The DBNull.Value would work for a SqlDataReader or a SqlParameter - but not for this object here.
marc_s
You're right, I started to optimize the condition check part, haven't looked at the line before. Mea culpa.
User
Yep, now you're talking! :-)
marc_s
+2  A: 

I suppose you can do it like this:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

If accountNumber is null it means it was DBNull not string :)

ppiotrowicz
Or `return (accountNumber as string) ?? string.Empty;` , with accountNumber still being an `object`. If you prefer to keep your database call on its own line.
Brian
+2  A: 

You can use C#'s null coalescing operator

return accountNumber ?? string.Empty;
Nathan Koop
+1 You beat me to it. :(
dss539
-1: That won't compile: the method returns a string and accountNumber is an object.
Joe
return Cmd.ExecuteScalar().ToString() ?? String.Empty;
Chaitanya
+12  A: 

With a simple generic function you can make this very easy. Just do this:

return ConvertFromDBVal<string>(accountNumber);

using the function:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value) {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}
rein
Yes, a function like this is the only practical solution. Any kind of in-line logic will fail after you have copied and pasted it a thousand times. :-)
Christian Hayter
this will not work if you try converting 1 to bool (Convert.ToBoolean(1) works fine tho)
roman m
@roman: so then we would want to have an additional check (prior to checking for null) that checks for a boolean type...
dboarman
@rein: awesome... +1 and bonus bounty for you. This should be the answer.
dboarman
+4  A: 

ExecuteScalar will return

  • null if there is no result set
  • otherwise the first column of the first row of the resultset, which may be DBNull.

If you know that the first column of the resultset is a string, then to cover all bases you need to check for both null and DBNull. Something like:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

The above code relies on the fact that DBNull.ToString returns an empty string.

If accountNumber was another type (say integer), then you'd need to be more explicit:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

If you know for sure that your resultset will always have at least one row (e.g. SELECT COUNT(*)...), then you can skip the check for null.

In your case the error message "Unable to cast object of type ‘System.DBNull’ to type ‘System.String`" indicates that the first column of your result set is a DBNUll value. This is from the cast to string on the first line:

string accountNumber = (string) ... ExecuteScalar(...);

Marc_s's comment that you don't need to check for DBNull.Value is wrong.

Joe
my resultset will not always return a row.
Saif Khan
A: 

String.Concat transform DBNull and null values to an empty string.

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

However, I think you loose something on code understandability

Andrea Parodi
A: 

There is another way to workaround this issue. How about modify your store procedure? by using ISNULL(your field, "") sql function , you can return empty string if the return value is null.

Then you have your clean code as original version.

Russel Yang