No se puede convertir el objeto del tipo 'System.DBNull' al tipo 'System.String`


109

Recibí el error anterior en mi aplicación. Aquí está el código original

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

Reemplacé con

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

¿Hay una mejor forma de evitar esto?


2
realmente deberías mirar la respuesta de @ rein, te ahorrará mucho tiempo a largo plazo
roman m

Respuestas:


90

Se puede utilizar una forma más corta:

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

EDITAR: No he prestado atención a ExecuteScalar. Realmente devuelve nulo si el campo está ausente en el resultado devuelto. Entonces usa en su lugar:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 

3
Eso no funcionará - el "accountNumber" no es un valor de la base de datos, sino una instancia de "objeto" normal de Plain Old .NET - debe verificar con el valor "nulo" normal. DBNull.Value funcionaría para un SqlDataReader o un SqlParameter, pero no para este objeto aquí.
marc_s

Tienes razón, comencé a optimizar la parte de verificación de condición, no he mirado la línea antes. Mea culpa.
Usuario

Hay un error tipográfico en su publicación que realmente no puedo editar porque la edición requiere que se cambien 6 caracteres. ¿Alguien puede cambiar accountNumber.TosString () a accountNumber.ToString ()
Eric

@marc_s Dependiendo del diseño de la base de datos / consulta, debe verificar cualquiera de ellos o incluso ambos. Si el DÓNDE no coincide con ninguna fila, obtendrá un null, si la fila seleccionada tiene NULLen esa columna, el valor de retorno es System.DBNull.
Alexander

En el primer caso, @Alexander menciona que no coincide con ninguna fila, puede confiar en Convert.ToString o cualquier otro método Convert si está de acuerdo con el valor que devuelven al convertir de nulo: cadena vacía para cadenas, 0 para valores numéricos, falso para booleano, MinValue para DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime

199

Con una función genérica simple puede hacer esto muy fácil. Solo haz esto:

return ConvertFromDBVal<string>(accountNumber);

usando la función:

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

1
Sí, una función como esta es la única solución práctica. Cualquier tipo de lógica en línea fallará después de que la haya copiado y pegado mil veces. :-)
Christian Hayter

3
esto no funcionará si intenta convertir 1 a bool (Convert.ToBoolean (1) funciona bien aunque)
roman m

@roman: entonces querríamos tener una verificación adicional (antes de verificar si hay nulo) que verifique un tipo booleano ...
IAbstract

1
Si desea o necesita utilizar las funciones de conversión, esto no funciona. Hay varios escenarios en los que podría preferir convertir a un elenco explícito. @romanm notó uno de ellos. Otro es cuando trabajas con decimales y te preocupas por los diferentes mecanismos de redondeo que utilizan Convert.ToInt32 e (int). El primero se redondea al valor par más cercano, mientras que el elenco explícito simplemente trunca el valor: stackoverflow.com/questions/1608801/… Si es posible, eliminaría los NULL de la mezcla, usando la función T-SQL ISNULL
Jaime

2
@Jaime Se supone que esta función actúa como una conversión implícita de un tipo de datos SQL a un tipo de datos C # / .NET. Si necesita una transmisión explícita, no use esta función, hágalo explícitamente en su lugar.
reinicio el

17

ExecuteScalar volverá

  • nulo si no hay un conjunto de resultados
  • de lo contrario, la primera columna de la primera fila del conjunto de resultados, que puede ser DBNull.

Si sabe que la primera columna del conjunto de resultados es una cadena, entonces, para cubrir todas las bases, debe verificar tanto el valor nulo como el DBNull. Algo como:

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

El código anterior se basa en el hecho de que DBNull.ToString devuelve una cadena vacía.

Si accountNumber fuera de otro tipo (digamos entero), entonces tendría que ser más explícito:

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

Si sabe con certeza que su conjunto de resultados siempre tendrá al menos una fila (por ejemplo, SELECT COUNT (*) ...), puede omitir la comprobación de nulo.

En su caso, el mensaje de error "No se puede convertir un objeto de tipo 'System.DBNull' en el tipo 'System.String`" indica que la primera columna de su conjunto de resultados es un valor DBNUll. Esto es del elenco a la cadena en la primera línea:

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

El comentario de Marc_s de que no es necesario verificar DBNull.Value es incorrecto.


mi conjunto de resultados no siempre devolverá una fila.
Saif Khan

6

Puede utilizar el operador de fusión nula de C #

return accountNumber ?? string.Empty;

-1: Eso no se compilará: el método devuelve una cadena y accountNumber es un objeto.
Joe

2
return Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya

return Cmd.ExecuteScalar (). ToString () hizo el trabajo por mí
Taran

3

Hay otra forma de solucionar este problema. ¿Qué tal modificar el procedimiento de su tienda? al usar ISNULL (su campo, "") función sql, puede devolver una cadena vacía si el valor de retorno es nulo.

Entonces tienes tu código limpio como versión original.


3

Este es el método genérico que utilizo para convertir cualquier objeto que pueda ser un DBNull.Value:

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

uso:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

corta:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);

2

Supongo que puedes hacerlo así:

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

Si accountNumber es nulo, significa que era DBNull, no una cadena :)


O return (accountNumber as string) ?? string.Empty;, con accountNumber aún siendo un object. Si prefiere mantener su base de datos, llame a su propia línea.
Brian

1

String.Concat transforma DBNull y valores nulos en una cadena vacía.

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

    return String.Concat(accountNumber);

 }

Sin embargo, creo que pierde algo en la comprensibilidad del código


1
¿Qué pasa si escribes return "" + accountNumber;?
Zev Spitz

0

Desde que obtuve una instancia que no es nula y si comparé con DBNULL obtuve Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' excepción, y si intenté cambiar para comparar con NULL, simplemente no funcionó (ya que DBNull es un objeto) incluso esa es la respuesta aceptada.

Decidí usar simplemente la palabra clave 'es'. Entonces el resultado es muy legible:

data = (item is DBNull) ? String.Empty : item


-1

Utilizo una extensión para eliminar este problema, que puede ser o no lo que buscas.

Dice así:

public static class Extensions
{

    public String TrimString(this object item)
    {
        return String.Format("{0}", item).Trim();
    }

}

Nota:

¡Esta extensión no devuelve nullvalores! Si el artículo es nullo DBNull.Value , devolverá una cadena vacía.

Uso:

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.