2012-02-24 11 views
17

Si tengo un código similar al siguiente:Posible de usar ?? (el operador de fusión) con DBNull?

while(myDataReader.Read()) 
{ 
    myObject.intVal = Convert.ToInt32(myDataReader["mycolumn"] ?? 0); 
} 

que arroja el error:

Object cannot be cast from DBNull to other types.

definir intVal como int anulable no es una opción. ¿Hay alguna manera de que haga lo anterior?

+3

posible duplicado de [Manipulación DBNull en C#] (http: // stackoverflow .com/questions/2433155/handle-dbnull-in-c-sharp) – BoltClock

Respuesta

13

Se puede utilizar un método de extensión? (Escrito de la parte superior de mi cabeza)

public static class DataReaderExtensions 
{ 
    public static T Read<T>(this SqlDataReader reader, string column, T defaultValue = default(T)) 
    { 
     var value = reader[column]; 

     return (T)((DBNull.Value.Equals(value)) 
        ? defaultValue 
        : Convert.ChangeType(value, typeof(T))); 
    } 
} 

debe usarla como:

while(myDataReader.Read()) 
{ 
    int i = myDataReader.Read<int>("mycolumn", 0); 
} 
+0

Terminé yendo por la ruta del método de extensión. Me estaba dando un error con la parte 'Convert.ChangeType ...', así que hice algunos cambios menores. –

+0

¿Puede actualizar mi respuesta con los "cambios menores" que tuvo que hacer? –

+0

En realidad sigo teniendo problemas para hacer funcionar el casting. 'Convertir.ChangeType (reader [columna], typeof (T)) 'no se compila, así que cambié a este' (T) lector [columna] 'que arroja una excepción de tiempo de ejecución. ¿Algunas ideas? –

2

por qué no utilizar algo que no sea el operador nula coalescencia (DBNull.Value = null!):

int i = myDataReader["mycolumn"] == DBNull.Value ? 
      Convert.ToInt32(myDataReader["mycolumn"]) : 
      0; 

Siempre se puede envolverlo en un método de extensión ordenada:

public static T Read<T>(this DataReader reader, string column, T defaultVal) 
{ 
    if(reader[column] == DBNull.Value) return defaultVal; 
    return Convert.ChangeType(reader[column], typeof(T)); 
} 
+1

Consideré un ternario si pero '' 'parece más limpio si puedo lograrlo. –

+0

@AbeMiessler No creo que '??' funcione con 'DBNull'; en realidad no es 'nulo'. – dlev

+0

Lo consiguió al revés. También sugeriría que este método no es tan eficiente. – nawfal

1

No, sólo funciona para los nulos.

¿Qué tal un método de extensión en un objeto que busca DBNull y devuelve un valor predeterminado?

//may not compile or be syntactically correct! Just the general idea. 
public static object DefaultIfDBNull(this object TheObject, object DefaultValue) 
{ 
    if(TheObject is DBNull) 
     return DefaultValue; 
    return TheObject; 
} 
+0

¿Por qué no utilizar genéricos en lugar de 'objeto'? – dlev

+0

@dlev En el contexto de tratar con el lector de datos no genérico pensé que esto sería más fácil. Fue solo por encima de mi cabeza. La respuesta genérica de insta se ve bastante bien. – asawyer

5

¿Qué tal algo como:

object x = DBNull.Value; 
int y = (x as Int32?).GetValueOrDefault(); //This will be 0 

o en su caso:

int i = (myDataReader["mycolumn"] as Int32?).GetValueOrDefault(); 
+0

Como John Saunders señaló en su respuesta a la pregunta vinculada "posible duplicado", este enfoque tiene la desventaja de no generar una excepción si el tipo de datos de la columna cambia. Por ejemplo, si "mycolumn" tiene un valor "short" de 42, la variable "i" tendrá un valor de "0". Un lanzamiento directo generaría una excepción, llamando la atención sobre el tipo de desajuste entre el código y la base de datos. – phoog

+0

@phoog - Punto válido, supongo que cada método tiene sus pros y sus contras. Personalmente, me gusta mi base de datos y el código lo más estrechamente posible. Yo usaría un código 'int' en el código, o no permitiría' null' en la base de datos. –

6

¿Puede simplemente utilizar Int32.Tryparse?

int number; 
bool result = Int32.TryParse(myDataReader["mycolumn"].ToString(), out number); 

De acuerdo con la MSDN, number contendrá 0 si la conversión no

+1

+1, buena respuesta. Esto hubiera funcionado, pero el int que estoy tratando de asignar es una propiedad del objeto que no funciona con 'out'. –

+5

Esto no se compila, ¿o sí? El indexador del lector de datos devuelve una referencia de objeto, TryParse toma un parámetro de cadena. Por supuesto, puede llamar a 'ToString' en el objeto, pero es bastante ineficiente construir la representación de cadena de un int para que pueda analizarlo. Una conversión de unboxing sería mucho más eficiente. – phoog

+0

@phoog, buen punto. Si es nulo, ToString bombardearía de todos modos. Podría usar 'como cadena' en su lugar. –

10

Aquí es una opción más:

while (myDataReader.Read()) 
{ 
    myObject.intVal = (myDataReader["mycolumn"] as int? ?? 0); 
} 
+0

¡La mejor manera absolutamente! Gracias Edyn. – ClownCoder

+0

Brillante respuesta! Muy limpio. –

Cuestiones relacionadas