c# - ejemplo - ¿Cuál es la mejor manera de lidiar con DBNull?
how to connect to database in c# (14)
Con frecuencia tengo problemas para tratar DataRows
devuelto por SqlDataAdapters
. Cuando intento completar un objeto usando un código como este:
DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;
¿Cuál es la mejor manera de lidiar con DBNull''s
en este tipo de situación?
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();
Normalmente escribo mi propia clase ConvertDBNull que envuelve la clase convert incorporada. 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)
devuelve Convert.ToInt64(obj)
menos que obj sea DBNull, en cuyo caso devolverá 0.
Por alguna razón, he tenido problemas para verificar DBNull.Value, así que hice las cosas un poco diferentes y aproveché una propiedad dentro del objeto DataRow:
if (row.IsNull["fooColumn"])
{
value = string.Empty();
}
{
else
{
value = row["fooColumn"].ToString;
}
También puede probar con Convert.IsDBNull (MSDN) .
Si no usa tipos que aceptan valores nulos, lo mejor que puede hacer es verificar 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, puedes crear una clase convert con un método de conversión sobrecargado por tipo para que no tengas que rociar tu código con bloques if / else.
Sin embargo, insistiré en que los tipos que aceptan nulos son la mejor opción si puedes 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, use tipos que aceptan nulos.
- Para columnas que no pueden ser nulas en la base de datos, use tipos regulares.
Se hicieron tipos anulables para resolver este problema. Dicho esto, si usted está en una versión anterior del marco de trabajo o trabaja para alguien que no asimila los tipos que aceptan nulos, el ejemplo del código hará el truco.
Si tiene el control de la consulta que devuelve los resultados, puede usar ISNULL () para devolver valores no nulos como este:
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, tomar este enfoque puede solucionar el problema a través de toda su aplicación sin saturar su código.
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.
Entonces, suponiendo una columna Intlad anulable llamada "MyCol", donde queremos devolver -99 si la columna es nula, pero devuelve el valor entero si la columna no es nula:
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 más fácil de detectar errores:
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"]);
Si le preocupa obtener DBNull cuando espera cadenas, una opción es convertir todos los valores DBNull en DataTable en una cadena vacía.
Es bastante simple hacerlo, pero agregaría algunos gastos generales, especialmente si se trata de grandes DataTables. Consulte este enlace que muestra cómo hacerlo si está interesado
Brad Abrams publicó algo relacionado hace solo 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. Prefiere Nullable en su lugar".
Y aquí están mis dos centavos (del 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 ... ¿Alguna razón por la que no estás usando un ORM?
DBNull implementa .ToString () como todo lo demás. No necesito hacer cualquier cosa. En lugar del lanzamiento duro, llame al método .ToString () del objeto.
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 esta es la mejor práctica que estás buscando
También deberías mirar los métodos de extensión. Aquí hay algunos ejemplos para tratar con esta escena.
Lectura recomendada
Vale la pena mencionar que DBNull.Value.ToString()
es igual a String.Empty
Puedes usar esto a tu favor:
DataRow row = ds.Tables[0].Rows[0];
string value = row["name"].ToString();
Sin embargo, eso solo funciona para Strings, para todo lo demás usaría el método linq o un método de extensión. Para mí, he escrito un pequeño método de extensión que busca DBNull e incluso realiza el casting a través de Convert.ChangeType(...)
int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);
A menudo, cuando se trabaja con DataTables, tiene que lidiar con estos casos, donde el campo de la fila puede ser nulo o DBNull, normalmente me ocupo de esto:
string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;
El operador ''como'' devuelve nulo para el molde no válido, como DBNull para cadena, y el ''??'' devuelve el término a la derecha de la expresión si el primero es nulo.
Los tipos anulables son buenos, pero solo para los tipos que no son nulos para empezar.
Para hacer que un tipo "nulable" añada un signo de interrogación al tipo, por ejemplo:
int? value = 5;
También recomendaría usar la palabra clave " as
" en lugar de enviar. 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
- Si un tipo es anulable, la palabra clave "
as
" devuelvenull
si el valor esDBNull
. - Sin embargo, es ligeramente más rápido que el lanzamiento, aunque solo en ciertos casos . Esto por sí solo nunca es una buena razón para usar
as
, pero junto con la razón anterior es útil.
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
, el value
se convertirá en null
lugar de arrojar una excepción. Tenga en cuenta que si su consulta de DB cambia las columnas / tipos que se devuelven, el uso de as
hará que su código falle silenciosamente y haga que los valores sean null
lugar de arrojar la excepción apropiada cuando se devuelven datos incorrectos, por lo que se recomienda realizar pruebas para validar sus consultas de otras formas para garantizar la integridad de los datos a medida que evoluciona su base de código.