2009-10-03 18 views
5

Actualmente estoy usando un lector de datos sql (en vb.net) para extraer un objeto de artículo mediante un proceso almacenado desde una base de datos de SQL Server 2008. Parte de este objeto incluye las dos propiedades se muestran a continuación:¿Cómo devuelvo un valor a sqldatareader si el valor es nulo?

theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness")))) 
theArticle.Relevance = ((myReader.GetInt32(myReader.GetOrdinal("Relevance")))) 

Mi problema es que la veracidad y Relevance pueden devolver un valor nulo y esto está causando la función se caiga.

Creo que entiendo por qué. Estoy pidiendo un valor entero (getin32) y como se devuelve null falla.

¿Cómo acomodo el valor nulo de la base de datos para que no se caiga?

Respuesta

16

Puede verificar si una posición ordinal determinada es nula usando .IsDBNull() y luego hacer algo - p. fijar su valor a -1 o algo:

int myOrdinal = myReader.GetOrdinal("Truthfullness"); 

if(myReader.IsDBNull(myOrdinal)) 
{ 
    theArticle.Truthfulness = -1; 
} 
else 
{ 
    theArticle.Truthfulness = myReader.GetInt32(myOrdinal); 
} 

Como Mike Hofer señala en su respuesta, también se puede envolver toda esta lógica en un método de extensión:

public static class SqlDataReaderExtensions 
{ 
    public static int SafeGetInt32(this SqlDataReader reader, 
            string columnName, int defaultValue) 
    { 
     int ordinal = reader.GetOrdinal(columnName); 

     if(!reader.IsDbNull(ordinal)) 
     { 
      return reader.GetInt32(ordinal); 
     } 
     else 
     { 
      return defaultValue; 
     } 
    } 
} 

y luego usar sólo que " método SafeGetInt32" en su lugar:

theArticle.Truthfulness = myReader.SafeGetInt32("Truthfullness", -1); 

Marc

+0

o detectar la excepción y tratar con él – Mark

+1

Sí, se puede hacer eso también - pero evitando una excepción es mejor que la captura y el manejo de uno (en general) –

+1

@marc_s: de acuerdo con su comentario en mi respuesta. Eliminándolo Gracias por dejarlo en claro. Su comentario fue "No creo que esto funcione, ya que si la columna db es NULL, la llamada .GetInt32() fallará con una excepción: no obtendrá un valor NULL que luego podrá ingresar al" "operador ...." – Mahin

3

¿Ha comprobado, SqlDataReader.IsDBNul l Método? Probablemente algo como:

if(myReader.IsDBNull(myReader.GetOrdinal("Truthfulness")) 
theArticle.Truthfulness = string.Empty; 
else 
theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness")))) 
+0

bien, string.Empty no servirá de mucho si se trata de una propiedad int32 ...... –

1

Sabes, lidiar con esto todo el tiempo en Oracle. Para limpiar el código de arriba, escribí un conjunto de métodos de extensión para simplificar la operación:

using System.Data.OracleClient; 
public static class OracleDataReaderExtensions 
{ 
    public static int GetInt32(this OracleDataReader reader, string columnName, int defaultValue) 
    { 
     return reader.GetInt32(reader.GetOrdinal(columnName)) != DbNull.Value ? 
       reader.GetInt32(reader.GetOrdinal(columnName)) : 
       defaultValue; 
    } 
} 

Crear una sobrecarga separada para cada tipo que desea devolver. Principalmente trabajo con cadena, int, fecha y decimal. Recuerde YAGNI (no necesita trabajar con todos los tipos admitidos por el lector, solo aquellos que realmente usa).

Una clase de extensión como esta para SQL Server es realmente fácil de escribir, y VASTLY simplificará su trabajo. Confía en mí en eso. ¿Te mentiría? :)

+1

No creo que esto funcione, ya que si la columna en la base de datos es NULL, entonces la llamada a GetInt32() causará una excepción. No se puede comparar esa llamada a GetNnt32() con DBNull.Value y reaccionar sobre eso ... –

+0

En realidad, funciona. Lo uso todos los dias. –

+0

La razón por la que funciona es porque estamos usando el operador de cortocircuito?:. Si no es nulo, lo devolvemos. De lo contrario, devolvemos el valor predeterminado. –

0

Esto es lo que usamos en SQL Server y funciona como un encanto:

... 

    Dim X as Object = pbDr("TotAmt") 'dr is dim'ed as a DataReader 

... 

    Public Function pbDr(ByVal drName As String) As Object 

    Dim SQLError As SqlClient.SqlException 

    Dim IsNull As Boolean 

    Dim Ordinal, DispNbr As Integer 

    Try 
     Ordinal = dr.GetOrdinal(drName) 
     IsNull = dr.IsDBNull(Ordinal) 
     If IsNull Then 
     Dim Dbtype As String = dr.GetFieldType(Ordinal).ToString 
     If Dbtype = "System.String" Then 
      Return "" 
     ElseIf Dbtype = "System.Int32" _ 
     OrElse Dbtype = "System.Double" _ 
     OrElse Dbtype = "System.Decimal" _ 
     OrElse Dbtype = "System.Int16" Then 
      Return 0 
     Else 
      MsgBox("Print This Screen And Send To Support" _ 
      & "pbdr-Object = " & Dbtype, MsgBoxStyle.Critical) 
      Return "" 
     End If 
     Else 
     Return dr(Ordinal) 
     End If 

    Catch sqlerror 
     Call DispSQLError(SQLError, "pbDr") 
     pbDr = "" 
    End Try 

    End Function 
0

IsDBNull (int) es por lo general mucho más lento que el uso de métodos como GetSqlInt32 y luego comparar a DBNull.Value o utilizando su propio .IsNull Me gusta:

public static int Int32(this SqlDataReader r, int ord) 
    { 
     var t = r.GetSqlInt32(ord); 
     return t.IsNull ? default(int) : t.Value; 
    } 

Probé algunas soluciones de plantilla, pero en vano hasta ahora. El problema es que todos los tipos Sql (tipos SqlInt32 aquí) son en realidad estructuras y, aunque todos tienen la propiedad .Value, C# no tiene plantillas reales para manejar eso. También tienen su propia interfaz INullable que solo tiene .IsNull y no es compatible con Nyllable <>.

Sospecho que uno necesitaría un conjunto completo de tipos Sql como plantillas C# o agregar ICOnvertible a ellos para poder tener solo uno o dos métodos con plantillas.

Si alguien tiene una idea tal vez con un truco o dos funcional hablar :-)

1

Esta versión genérica puede ser de utilidad:

private T ValueOrDefault<T>(System.Data.IDataReader rdr, string columnName) 
    { 
     T vod = default(T); 
     try 
     { 
      int idx = rdr.GetOrdinal(columnName); 
      if (!rdr.IsDBNull(idx)) 
       return (T)rdr[idx]; 
     } 
     catch (IndexOutOfRangeException) { } 

     return vod; 
    } 

podría extenderse a coger InvalidCastException, o utilizar Convert .ChangeType en lugar de lanzar?

+0

Me gusta esta opción a excepción de la declaración catch IndexOutOfRangeException. Creo que le gustaría saber lo más rápido posible si la estructura del esquema ha cambiado en lugar de llevar los valores predeterminados a otras partes de su sistema. –

Cuestiones relacionadas