2010-03-12 21 views
35

¿Existe una forma mejor/más limpia de hacerlo?Manejar DBNull en C#

int stockvalue = 0; 
if (!Convert.IsDBNull(reader["StockValue"])) 
    stockvalue = (int)reader["StockValue"]; 
+0

También debe tener en cuenta los métodos de extensión. Aquí hay algunos ejemplos proporcionados para otras formas posibles: http://shahanayyub.wordpress.com/2012/10/04/best-practice-to-check-for-dbnull-using-net/ – NeverHopeless

Respuesta

6

Sí, puedes usar int? De esta manera puede tener un valor predeterminado de null en lugar de 0. Como el resultado de stockvalue potencialmente podrían ser 0 no hay confusión en cuanto a si la base de datos de 0 o nulo. Por ejemplo, como este (pre nullable) tuvimos una inicialización predeterminada de -1 para representar que no se asignó ningún valor. Personalmente, pensé que esto era un poco peligroso porque si olvidas configurarlo en -1, hay un problema de corrupción de datos que puede ser realmente difícil de rastrear.

http://msdn.microsoft.com/en-us/library/2cf62fcy(VS.80).aspx

int? stockvalue = null; 

if (!Convert.IsDBNull(reader["StockValue"])) 
    stockvalue = (int)reader["StockValue"]; 

//Then you can check 

if(stockValue.HasValue) 
{ 
    // do something here. 
} 
+2

+1 para el tipo de datos correcto para usar. Sin embargo, IIRC, 'DBNull' todavía tiene que convertirse a' null' con un poco de comprobación. –

+1

use int? ¿cómo? Simplemente lanzando el lector ["StockValue"] a (int?) Lanzará una excepción cuando el lector ["StockValue"] es DBNull –

+0

puede usar int? para que no tenga que usar una inicialización predeterminada en 0. – kemiller2002

0

utilizar el tipo de Nullable<int> ... int? por sus siglas en

1

Ésta es una forma.

int stockvalue = Convert.IsDbNull(reader["StockValue"]) ? 0 : (int)reader["StockValue"]; 

También es posible usar TryParse

int stockvalue = 0 
Int32.TryParse(reader["StockValue"].ToString(), out stockvalue); 

Háganos saber de qué manera funciona para usted

+0

No me importa el voto negativo, simplemente deje un comentario que diga cómo podría mejorar esta respuesta. –

+1

Definitivamente no hay ninguna razón para un DV aquí ya que su respuesta es una solución correcta. –

+0

Sí, nada de malo en eso. Te voté para equilibrarlo. :-) –

1

Usted podría hacer esta conversión directamente en su DB-consulta, evitando así el caso especial alltogether.

Pero yo no llamaría a ese "limpiador", a menos que pueda usar ese formulario de manera consistente en su código, ya que perdería información al devolver '0' en lugar de NULL de la base de datos.

+0

El argumento sobre perder información tiene sentido. En una aplicación de base de datos, el valor de "0" es diferente de NULL. Uno denota que no tiene ningún valor en absoluto, y uno es un valor que simplemente pasa a ser cero. –

0

No realmente. Se podría encapsular en un método:

public int getDBIntValue(object value, int defaultValue) { 
    if (!Convert.IsDBNull(value)) { 
    return (int)value; 
    } 
    else { 
    return defaultValue; 
    } 

y lo llaman así:

stockVaue = getDBIntVaue(reader["StockValue"], 0); 

O usted podría utilizar coalesce en su consulta para forzar el valor vuelto a ser no nulo.

Editar errores de código tonto corregidos en función de los comentarios recibidos.

+0

Y los votos a favor son porque ...? – Ray

+3

@Ray: mire su código, y usted votará menos. –

+1

(No te recomendé, pero) Por un lado, pasaste el lector ["StockValue"] en el parámetro "valor", pero luego ignoró el parámetro "valor" dentro de la función y usaste "reader [" StockValue "] "en cambio, entonces no compilará". –

10
int? stockvalue = (int?)(!Convert.IsDBNull(result) ? result : null); 

Una posible solución para que se asegure de que DBNull se transfiera a su código. Para nuestro grupo, como una mejor práctica, tratamos de no permitir columnas NULL en la base de datos a menos que realmente se necesite. Hay más sobrecarga en la codificación para manejarlo, y algunas veces solo repensar el problema hace que no sea necesario.

+0

Creo que esta solución es la más clara y flexible. Fui con algo casi idéntico pero no permitiendo nulos: Int32 aNum = Convert.IsDBNull (row ["stockvalue"])? 0: Convert.ToInt32 (row ["stockvalue"]) – Frug

28

A mi modo de manejar esto es

int? stockvalue = reader["StockValue"] as int?; 

muy sencillo, limpio y una línea. Si por alguna razón no puedo tener un valor nulo (que normalmente no me gusta, ya que preferiría saber si un valor tiene significado o si fue unificado para un tipo primitivo) lo haría:

int stockvalue = (reader["StockValue"] as int?).GetValueOrDefault(-1); 
+0

Ahh, recibí una excepción usando '(int?) Reader [" ColumnName "]' y comencé a buscar la forma correcta de hacerlo. Esto debe ser aceptado como respuesta. – Anlo

+0

Excelente. Esto también preserva el estado DBNull en caso de que uno esté interesado en saber eso. Algunas otras soluciones aquí descartan esto. – Jonas

6

Si bien es conveniente hacer referencia a reader["StockValue"], no es muy eficiente. Tampoco está fuertemente tipado, ya que devuelve el tipo object.

En cambio, dentro de su código, hacer algo como esto:

int stockValueOrdinal = reader.GetOrdinal("StockValue"); 
int? stockValue = reader.IsDbNull(stockValueOrdinal) ? 
    null : 
    reader.GetInt32(stockValueOrdinal); 

Por supuesto, lo mejor es conseguir todos los ordinales de una sola vez, luego usarlos en todo el código.

+0

@John: en su comentario sobre mi respuesta, me preguntó "¿y si la columna cambiara a doble?". El código que proporcionó aquí arrojará una excepción si la columna tiene un valor que no es de tipo int. (también, detalle menor: no hay método .GetInt() en un lector de datos. Debería ser .GetInt32()) –

+2

@Philippe: lanzar una excepción es _precisamente_ lo que el código debería hacer si el tipo de columna cambia y el código no lo hace . Regresar a cero cuando algo serio está mal es una muy mala idea. –

+0

@Philippe: gracias por atrapar el error tipográfico. –

55

El más corto (en mi humilde opinión) es:

int stockvalue = (reader["StockValue"] as int?) ?? 0; 

Explicación:

  • Si lector [ "StockValue"] es de tipo int, se devolverá el valor, y la "??" operador devuelve el resultado
  • Si lector [ "StockValue"] no es del tipo int (por ejemplo DBNull), se devuelve un valor nulo, y el "??" el operador devolverá el valor 0 (cero).
+2

@Philippe: ¿qué ocurre si el tipo de columna se cambia a Doble o algo así? –

+1

+1 - Esta es la forma en que escribiría el código, aunque si un valor nulo es válido no es necesario el valor predeterminado; 'int? stockvalue = reader ["StockValue"] como int? ' – stevehipwell

+2

@John: devolvería 0. Pero no importa qué solución elijas, si una columna cambia el tipo de datos, estás jodido, a menos que hagas una llamada a Convert. ToInt32() o algo. –

0

que tienen dos siguientes métodos de extensión en mi proyecto:

public static T GetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor) 
     where T : class 
    { 
     T value; 
     if (dataReader.TryGetValueSafe(columnName, valueExtractor, out value)) 
     { 
      return value; 
     } 

     return null; 
    } 

    public static bool TryGetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor, out T value) 
    { 
     int ordinal = dataReader.GetOrdinal(columnName); 

     if (!dataReader.IsDBNull(ordinal)) 
     { 
      // Get value. 
      value = valueExtractor.Invoke(dataReader, ordinal); 

      return true; 
     } 

     value = default(T); 
     return false; 
    } 

El uso puede ser así:

string companyName = dataReader.GetValueSafe("CompanyName", (reader, ordinal) => reader.GetString(ordinal)); 
+1

Interesante pero muy prolijo, creé una clase similar a esto cuando trabajé con DataReaders extensamente pero lo hice mucho más conciso que funcionó como 'reader.Get (" ReaderField ")' y funcionó usando el método 'ChangeType' –

+0

Bueno idea +1, pero creo que mi implementación sería un poco más rápida. –

10

me escribió un método de extensión hace varios días. Mediante su uso sólo podría hacer:

int? stockvalue = reader.GetValue<int?>("StockValue"); 

Aquí está el método de extensión (modificar para adaptarse a sus necesidades):

public static class ReaderHelper 
{ 
    public static bool IsNullableType(Type valueType) 
    { 
     return (valueType.IsGenericType && 
      valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))); 
    } 

    public static T GetValue<T>(this IDataReader reader, string columnName) 
    { 
     object value = reader[columnName]; 
     Type valueType = typeof(T); 
     if (value != DBNull.Value) 
     { 
      if (!IsNullableType(valueType)) 
      { 
       return (T)Convert.ChangeType(value, valueType); 
      } 
      else 
      { 
       NullableConverter nc = new NullableConverter(valueType); 
       return (T)Convert.ChangeType(value, nc.UnderlyingType); 
      } 
     } 
     return default(T); 
    } 
} 
+2

Me encanta C#, y tengo un código similar a este en un lugar o 2, pero es absolutamente retardado que alguna vez tengamos que escribir código como este. Espero que con continuos avances en el DLR haya un día en que nunca tengamos que escribir un código como este para hacer un tipo de disputa. –

0
int? stockValue = reader["StockValue"] == null || reader["StockValue"] == DBNull.Value ? null : (int?)reader["StockValue"]; 
3
int stockvalue = reader["StockValue"] != DbNull.Value ? Convert.ToInt32(reader["StockValue"]) : 0; 
Cuestiones relacionadas