2012-09-07 21 views
5

Actualmente estoy usando este método para leer datos de DataReader -método genérico para leer datos de DataReader

private T GetValue<T>(object obj) 
{ 
    if (typeof(DBNull) != obj.GetType()) 
    { 
     return (T)obj; 
    } 
    return default(T); 
} 

llamada anterior método -

GetValue<int>(dataReader["columnName1"]) 
GetValue<string>(dataReader["columnName2"]) 
GetValue<float>(dataReader["columnName3"]) 

Sin embargo esto no funciona cuando columnName3 es tener como valores 7200000 con error
Invalid Cast Exception.

Estoy pensando en modificar mi método para reemplazar -

return (T)obj; 

con

return (T)Convert.ChangeType(obj, typeof(T)); 

Pero mirando hacia adelante para una mejor forma que este cambio implicará la conversión de tipos dos veces.
¿Alguna idea mejor?

¡Gracias!

+0

Qué tipo de datos es columnName3 en la base de datos? – RQDQ

+0

¿has visto Linq to Dataset? –

+0

@RQDQ, es el tipo de datos SQL Server 'real'. Intenté replicar en la aplicación de la consola con el siguiente código: object objValue = 7200000; float floatValue = (float) objValue; // falla aquí – iniki

Respuesta

4

Un método genérico tiene la ventaja de que puede reducir una gran cantidad de código inflado, pero de lo contrario producir su propio contenedor para cada tipo de datos le brinda la flexibilidad de tener un manejo personalizado. Y muy probablemente sus consultas db tendrán un efecto notable en el rendimiento que el modo de recuperación.

Le sugiero que escriba un conjunto de sus propios métodos de extensión que tener un enfoque genérico. La extensión del método en IDataReader le da la ventaja de no propagar los métodos en sub tipos de objetos completos. He tenido que manejar los tipos individualmente ya que varios conectores se comportaron de manera diferente, especialmente con el tipo Guid. También es difícil saber si el lector de datos lee el valor 0 o DBNull cuando devuelve 0 para ambos casos. Digamos que hay un campo de enumeración en su tabla con un valor nulo. ¿Por qué querrías que se leyera como la primera enumeración?

Sólo tiene que llamar:

dataReader.GetInt("columnName1") 
dataReader.GetString("columnName3") 
dataReader.GetFloat("columnName3") 

Y los métodos:

public static int? GetInt(this IDataReader r, string columnName) 
{ 
    var i = r[columnName];  
    if (i.IsNull()) 
     return null; //or your preferred value 

    return (int)i; 
} 

public static bool IsNull<T>(this T obj) where T : class 
{ 
    return (object)obj == null || obj == DBNull.Value; 
} 

Y del mismo modo,

public static string GetString(this IDataReader r, string columnName) 
{ 
} 

public static float GetFloat(this IDataReader r, string columnName) 
{ 
} 

Si realmente desea una función genérica que puede tener también.

public static T Get<T>(this IDataReader r, string columnName, T defaultValue = default(T)) 
{ 
    var obj = r[columnName];  
    if (obj.IsNull()) 
     return defaultValue; 

    return (T)obj; 
} 

Así lo llaman

dataReader.Get<int>(1); //if DBNull should be treated as 0 
dataReader.Get<int?>(1); //if DBNull should be treated as null 
dataReader.Get<int>(1, -1); //if DBNull should be treated as a custom value, say -1 

Dicho esto, el error se debe a que usted no está lanzando con el tipo correcto como se ha señalado en los comentarios. Podría haber ido con controles incorporados en DBNull, pero no para evitar que los datos se lean varias veces desde el lector, inspired from this curious case of microoptimization

0

Comenzando con .NET Framework 4.5

static class SqlReaderExtension 
{ 
    public static async Task<T> ReadAsync<T>(this SqlDataReader reader, string fieldName) 
    { 
     if (reader == null) throw new ArgumentNullException(nameof(reader)); 
     if (string.IsNullOrEmpty(fieldName)) 
      throw new ArgumentException("Value cannot be null or empty.", nameof(fieldName)); 

     int idx = reader.GetOrdinal(fieldName); 
     return await reader.GetFieldValueAsync<T>(idx); 
    } 
} 

y luego

string result = await reader.ReadAsync<string>("FieldName"); 
Cuestiones relacionadas