2008-08-25 9 views
41
de DBNull

frecuentemente tengo problemas con DataRows devuelto desde SqlDataAdapters. Cuando intento para rellenar un objeto utilizando un código como éste:Cuál es la mejor manera de lidiar con

DataRow row = ds.Tables[0].Rows[0]; 
string value = (string)row; 

¿Cuál es la mejor manera de tratar con DBNull's en este tipo de situación.

+0

cerrar: [most-efficient-way-to-check-for-dbnull-and-then-assign-to-a-variable] (http://stackoverflow.com/questions/221582/most-efficient-way -to-check-for-dbnull-and-then-assign-to-a-variable) – nawfal

Respuesta

34

Los tipos anulables son buenos, pero solo para los tipos que no son nulos para empezar.

para hacer un tipo "anulable" añadir un signo de interrogación al tipo, por ejemplo:

int? value = 5; 

También recomendaría el uso de la "as" palabra clave en lugar de fundición. Solo puede usar la palabra clave "como" en los tipos que aceptan nulos, así que asegúrese de incluir elementos que ya pueden contener nulos (como cadenas) o use tipos que aceptan valores nulos como se mencionó anteriormente. El razonamiento para esto es

  1. Si un tipo es anulable, el "as" palabra clave vuelve null si un valor es DBNull.
  2. Es ever-so-slightly faster than casting aunque only in certain cases. Esto por sí solo nunca es motivo suficiente para usar as, pero junto con el motivo anterior es útil.

te recomiendo hacer algo como esto

DataRow row = ds.Tables[0].Rows[0]; 
string value = row as string; 

En el caso anterior, si row vuelve como DBNull, entonces se convertirá en valuenull en lugar de lanzar una excepción. Tenga en cuenta que si su consulta DB cambia las columnas/tipos que se devuelven, utilizando as hará que su código falle silenciosamente y haga que los valores sean simples null en lugar de arrojar la excepción apropiada cuando se devuelven datos incorrectos, por lo que se recomienda que tenga pruebas en lugar de validar sus consultas de otras formas para garantizar la integridad de los datos a medida que evoluciona su base de código.

6

Agregue una referencia a System.Data.DataSetExtensions, que agrega soporte Linq para consultar tablas de datos.

Esto sería algo así como:

string value = (
    from row in ds.Tables[0].Rows 
    select row.Field<string>(0)).FirstOrDefault(); 
+2

+1: esta respuesta debería ser más votada y probablemente también aceptada, definitivamente row.Field ("column_name") es la más eficiente. , solución legible y limpia para tratar con null (DBNull.Value). También hay row.SetField ("column_name", value) para escribir el valor en la fila. –

2

suelo escribir mi propia clase ConvertDBNull que envuelve la clase Convert incorporado. Si el valor es DBNull, devolverá nulo si es un tipo de referencia o el valor predeterminado si es un tipo de valor. Ejemplo: - ConvertDBNull.ToInt64(object obj) vuelve Convert.ToInt64(obj) menos obj es DBNull en cuyo caso se devolverá 0.

21

Si no está utilizando tipos anulables, lo mejor que puede hacer es comprobar para ver si el valor de la columna es DBNull. Si es DBNull, establezca su referencia a lo que usa para null/empty para el tipo de datos correspondiente.

DataRow row = ds.Tables[0].Rows[0]; 
string value; 

if (row["fooColumn"] == DBNull.Value) 
{ 
    value = string.Empty; 
} 
else 
{ 
    value = Convert.ToString(row["fooColumn"]); 
} 

Como dijo Manu, puede crear una clase de conversión con un método de conversión según el tipo de sobrecarga por lo que no tiene que la pimienta con su código if/else bloques.

Sin embargo, haré hincapié en que los tipos que aceptan nulo son la mejor opción si puede usarlos. El razonamiento es que con tipos que no admiten nulos, vas a tener que recurrir a "números mágicos" para representar nulo. Por ejemplo, si está asignando una columna a una variable int, ¿cómo va a representar DBNull? A menudo no puede usar 0 porque 0 tiene un significado válido en la mayoría de los programas. A menudo veo que las personas asignan DBNull a int.MinValue, pero eso también podría ser problemático. Mi mejor consejo es este:

  • Para las columnas que pueden ser nulas en la base de datos, utilice tipos que aceptan valores de nulo.
  • Para columnas que no pueden ser nulas en la base de datos, use tipos normales.

Se hicieron tipos anulables para resolver este problema. Dicho esto, si usted está en una versión anterior del marco o si trabaja para alguien que no asimila los tipos que aceptan nulos, el ejemplo del código hará el truco.

+0

La línea if (row ["fooColumn"] == DBNull.Value) funciona, pero no es correcta. No se define DBNull.Value debe implementarse como un patrón de Singleton. Una línea mejor sería: if (row ["fooColumn"] es DBNull) – doekman

+0

@doekman - En realidad, DBNull * es * una clase singleton. Para citar a MSDN: "DBNull es una clase singleton, lo que significa que solo puede existir esta instancia [DBNull.Value] de esta clase". http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx – Greg

+2

Si desea tratar dbnull como fila de cadena vacía ["fooColumn"]. ToString() lo hará. – samir105

1

Por alguna razón que he tenido problemas con hacer una verificación contra DBNull.Value, por lo que he hecho las cosas un poco diferente y ha movilizado una propiedad dentro del objeto DataRow:

if (row.IsNull["fooColumn"]) 
{ 
    value = string.Empty(); 
} 
{ 
else 
{ 
    value = row["fooColumn"].ToString; 
} 
5

Si usted tiene el control de la consulta que está regresando los resultados, se puede utilizar ISNULL() para devolver valores no nulos como esto:

SELECT 
    ISNULL(name,'') AS name 
    ,ISNULL(age, 0) AS age 
FROM 
    names 

Si su situación puede tolerar estos valores mágicos para sustituir a NULL, este enfoque puede solucionar el problema a través de toda su aplicación sin saturar su código.

8

Siempre me pareció claro, conciso y libre de problemas usando una versión de la verificación If/Else, solo con el operador ternario. Mantiene todo en una fila, incluida la asignación de un valor predeterminado si la columna es nula.

Así, suponiendo una columna anulable Int32 llamado "MyCol", donde queremos volver -99 si la columna es nula, pero devolver el valor entero si la columna no es nulo:

return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]); 

Es el mismo método que el ganador de If/Else anterior - Pero he descubierto que si lees varias columnas desde un lector de datos, es una verdadera ventaja tener todas las líneas de lectura de columna una debajo de otra, alineadas, ya que es más fácil errores puntuales:

Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]); 
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]); 
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]); 
0

Si le preocupa obtener DBNull cuando se espera g cadenas, una opción es convertir todos los valores DBNull en DataTable en una cadena vacía.

Es bastante simple de hacerlo, pero agregaría algunos gastos generales, especialmente si se trata de grandes DataTables. Marque esta link que muestra cómo hacerlo si está interesado

+0

Es importante saber la diferencia entre un nulo y una cadena vacía. Por ejemplo, establecer un valor en una cadena vacía en lugar de un valor que nunca se establece. – Mykroft

1

Brad Abrams publicado algo relacionado hace apenas un par de días http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

En Resumen "evitar el uso de System.DBNull. Prefiero anulable en su lugar."

Y aquí es mi granito de arena (de código no probado :))

// Or if (row["fooColumn"] == DBNull.Value) 
if (row.IsNull["fooColumn"]) 
{ 
    // use a null for strings and a Nullable for value types 
    // if it is a value type and null is invalid throw a 
    // InvalidOperationException here with some descriptive text. 
    // or dont check for null at all and let the cast exception below bubble 
    value = null; 
} 
else 
{ 
    // do a direct cast here. dont use "as", "convert", "parse" or "tostring" 
    // as all of these will swallow the case where is the incorect type. 
    // (Unless it is a string in the DB and really do want to convert it) 
    value = (string)row["fooColumn"]; 
} 

Y una pregunta ... algún motivo no está utilizando un ORM?

+0

Estamos usando ORM ahora. En el momento en que no estábamos – Mykroft

+0

* ¿Alguna razón por la que no está usando un ORM? * Uso ADO sin procesar.NET para el rendimiento. Intenta fusionar 1 millón de registros con EF o NH. –

3

DBNull implementa .ToString() .. como todo lo demás No hay necesidad de hacer nada en lugar de la fundición dura, llamada el método del objeto .ToString()

DataRow row = ds.Tables[0].Rows[0]; 
string value; 

if (row["fooColumn"] == DBNull.Value) 
{ 
    value = string.Empty; 
} 
else 
{ 
    value = Convert.ToString(row["fooColumn"]); 
} 

esto se convierte en:.

DataRow row = ds.Tables[0].Rows[0]; 
string value = row.ToString() 

DBNull.ToString() devuelve String.Empty

me imagino que es la mejor práctica que busca

+0

Esto no funciona si el valor no es una cadena. Estaba buscando una respuesta de caso general. – Mykroft

+0

Estoy de acuerdo, sí, es bueno si las cadenas lo mantienen simple, pero otros tipos no funcionarían, p. tratando de hacer bool.Parse (row ["fooColumn"]. ToString()). – PeteT

1

Usted también debe mirar a los métodos de extensión. Here son algunos ejemplos para tratar con esta escena.

Recomendado read

3

Vale la pena mencionar, que DBNull.Value.ToString() es igual a String.Empty

Usted puede usar esto para su ventaja:

DataRow row = ds.Tables[0].Rows[0]; 
string value = row["name"].ToString(); 

Sin embargo, esto sólo funciona para cuerdas, para todo lo demás me usaría la forma linq o un método de extensión. En cuanto a mí, he escrito un poco de método de extensión que comprueba DBNull e incluso hace la colada a través de Convert.ChangeType(...)

int value = row.GetValueOrDefault<int>("count"); 
int value = row.GetValueOrDefault<int>("count", 15); 
2

A menudo, cuando se trabaja con tablas de datos que tiene que hacer frente a estos casos, en los que el campo de fila puede ser null o DBNull, normalmente lidiar con eso así:

string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty; 

el 'como' operador devuelve nulo para el elenco de inválidos, como DBNull de cadena y el '??' devuelve el término a la derecha de la expresión si el primero es nulo.

Cuestiones relacionadas