Lector de datos SQL: manejo de valores de columna nulos

Estoy usando un lector de datos SQL para construir POCO desde una base de datos. El código funciona, excepto cuando encuentra un valor nulo en la base de datos. Por ejemplo, si la columna FirstName en la base de datos contiene un valor nulo, se lanza una excepción.

employee.FirstName = sqlreader.GetString(indexFirstName); 

¿Cuál es la mejor manera de manejar valores nulos en esta situación?

IsDBNull verificar IsDBNull :

 if(!SqlReader.IsDBNull(indexFirstName)) { employee.FirstName = sqlreader.GetString(indexFirstName); } 

Esa es su única forma confiable de detectar y manejar esta situación.

Envolví esas cosas en los métodos de extensión y tienden a devolver un valor predeterminado si la columna es realmente null :

 public static string SafeGetString(this SqlDataReader reader, int colIndex) { if(!reader.IsDBNull(colIndex)) return reader.GetString(colIndex); return string.Empty; } 

Ahora puedes llamarlo así:

 employee.FirstName = SqlReader.SafeGetString(indexFirstName); 

y nunca más tendrás que preocuparte por una excepción o un valor null .

Deberías usar el operador as combinado con el ?? operador para los valores predeterminados. Los tipos de valor deberán leerse como anulables y recibir un valor predeterminado.

 employee.FirstName = sqlreader[indexFirstName] as string; employee.Age = sqlreader[indexAge] as int? ?? default(int); 

El operador as maneja la conversión, incluida la comprobación de DBNull.

Para una cadena, simplemente puede convertir la versión del objeto (se accede usando el operador de la matriz) y terminar con una cadena nula para los nulos:

 employee.FirstName = (string)sqlreader[indexFirstName]; 

o

 employee.FirstName = sqlreader[indexFirstName] as string; 

Para enteros, si lanzas a un int nullable, puedes usar GetValueOrDefault ()

 employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault(); 

o el operador nulo-coalescente ( ?? ).

 employee.Age = (sqlreader[indexAge] as int?) ?? 0; 

IsDbNull(int) suele ser mucho más lento que utilizar métodos como GetSqlDateTime y luego comparar con DBNull.Value . Pruebe estos métodos de extensión para SqlDataReader .

 public static T Def(this SqlDataReader r, int ord) { var t = r.GetSqlValue(ord); if (t == DBNull.Value) return default(T); return ((INullable)t).IsNull ? default(T) : (T)t; } public static T? Val(this SqlDataReader r, int ord) where T:struct { var t = r.GetSqlValue(ord); if (t == DBNull.Value) return null; return ((INullable)t).IsNull ? (T?)null : (T)t; } public static T Ref(this SqlDataReader r, int ord) where T : class { var t = r.GetSqlValue(ord); if (t == DBNull.Value) return null; return ((INullable)t).IsNull ? null : (T)t; } 

Úselos así:

 var dd = r.Val(ords[4]); var ii = r.Def(ords[0]); int nn = r.Def(ords[0]); 

Una forma de hacerlo es comprobar si hay nulos en db:

 employee.FirstName = (sqlreader.IsDBNull(indexFirstName) ? "" : sqlreader.GetString(indexFirstName)); 

No creo que haya un valor de columna NULL , cuando las filas se devuelven dentro de un lector de datos usando el nombre de la columna.

Si haces datareader["columnName"].ToString(); siempre le dará un valor que puede ser una cadena vacía ( String.Empty si necesita comparar).

Usaría lo siguiente y no me preocuparía demasiado:

 employee.FirstName = sqlreader["columnNameForFirstName"].ToString(); 

Esta solución depende menos del proveedor y funciona con un SQL, OleDB y MySQL Reader:

 public static string GetStringSafe(this IDataReader reader, int colIndex) { return GetStringSafe(reader, colIndex, string.Empty); } public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue) { if (!reader.IsDBNull(colIndex)) return reader.GetString(colIndex); else return defaultValue; } public static string GetStringSafe(this IDataReader reader, string indexName) { return GetStringSafe(reader, reader.GetOrdinal(indexName)); } public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue) { return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue); } 

reader.IsDbNull(ColumnIndex) funciona como dice la mayoría de las respuestas.

Y quiero mencionar que si trabaja con nombres de columna, simplemente comparar tipos puede ser más cómodo.

 if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic } 

Lo que tiendo a hacer es reemplazar los valores nulos en la instrucción SELECT con algo apropiado.

 SELECT ISNULL(firstname, '') FROM people 

Aquí reemplazo cada nulo con una cadena en blanco. Su código no arrojará un error en ese caso.

Compruebe sqlreader.IsDBNull(indexFirstName) antes de intentar leerlo.

Puede escribir una función genérica para verificar Null e incluir el valor predeterminado cuando es NULL. Llamar esto cuando lea Datareader

 public T CheckNull(object obj) { return (obj == DBNull.Value ? default(T) : (T)obj); } 

Al leer el uso del lector de datos

  while (dr.Read()) { tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon(); Bpn.BPN_Date = CheckNull(dr["BPN_Date"]); Bpn.Cust_Backorder_Qty = CheckNull(dr["Cust_Backorder_Qty"]); Bpn.Cust_Min = CheckNull(dr["Cust_Min"]); } 

Creo que querrías usar:

 SqlReader.IsDBNull(indexFirstName) 

cómo sobre la creación de métodos auxiliares

Para cadena

 private static string MyStringConverter(object o) { if (o == DBNull.Value || o == null) return ""; return o.ToString(); } 

Uso

 MyStringConverter(read["indexStringValue"]) 

Para Int

  private static int MyIntonverter(object o) { if (o == DBNull.Value || o == null) return 0; return Convert.ToInt32(o); } 

Uso

 MyIntonverter(read["indexIntValue"]) 

Fecha

 private static DateTime? MyDateConverter(object o) { return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o); } 

Uso

 MyDateConverter(read["indexDateValue"]) 

Nota: para DateTime declara varialbe como

 DateTime? variable; 

Usamos una serie de métodos estáticos para extraer todos los valores de nuestros lectores de datos. Entonces, en este caso, llamaríamos a DBUtils.GetString(sqlreader(indexFirstName)) El beneficio de crear métodos estáticos / compartidos es que no tiene que hacer las mismas comprobaciones una y otra vez …

Los métodos estáticos contendrían código para verificar nulos (ver otras respuestas en esta página).

Una vieja pregunta, pero tal vez alguien todavía necesita una respuesta

en realidad, trabajé alrededor de este tema así

Para int:

 public static object GatDataInt(string Query, string Column) { SqlConnection DBConn = new SqlConnection(ConnectionString); if (DBConn.State == ConnectionState.Closed) DBConn.Open(); SqlCommand CMD = new SqlCommand(Query, DBConn); SqlDataReader RDR = CMD.ExecuteReader(); if (RDR.Read()) { var Result = RDR[Column]; RDR.Close(); DBConn.Close(); return Result; } return 0; } 

lo mismo para la cadena simplemente devuelve “” en lugar de 0 ya que “” es una cadena vacía

para que pueda usarlo como

 int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?; 

y

 string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string; 

muy flexible para que pueda insertar cualquier consulta para leer cualquier columna y nunca volverá con el error

Estoy utilizando el código que figura a continuación para manejar celdas nulas en una hoja de Excel que se lee en una tabla de datos.

 if (!reader.IsDBNull(2)) { row["Oracle"] = (string)reader[2]; } 
 private static void Render(IList list, IDataReader reader) { while (reader.Read()) { listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null; //没有这一列时,让其等于null list.Add(listData); } reader.Close(); } 

y / o usar operador ternario con asignación:

 employee.FirstName = rdr.IsDBNull(indexFirstName))? String.Empty: rdr.GetString(indexFirstName); 

reemplace el valor predeterminado (cuando sea nulo) según corresponda para cada tipo de propiedad …

Este método depende de indexFirstName, que debería ser el ordinal de columna basado en cero.

 if(!sqlReader.IsDBNull(indexFirstName)) { employee.FirstName = sqlreader.GetString(indexFirstName); } 

Si no conoce el índice de la columna pero no quiere verificar un nombre, puede usar este método de extensión en su lugar:

 public static class DataRecordExtensions { public static bool HasColumn(this IDataRecord dr, string columnName) { for (int i=0; i < dr.FieldCount; i++) { if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase)) return true; } return false; } } 

Y usa el método así:

 if(sqlReader.HasColumn("FirstName")) { employee.FirstName = sqlreader["FirstName"]; } 

Puede usar el operador condicional:

 employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : ""; 

siempre puedes verificar esto

 if(null !=x && x.HasRows) { ....}