2012-09-07 10 views
5

Attualmente sto usando questo metodo per leggere i dati da DataReader -metodo generico per leggere i dati da DataReader

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

chiamata sopra Metodo -

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

Tuttavia, questo non riesce quando columnName3 sta avendo come valori 7200000 con errore
Invalid Cast Exception.

Sto pensando di modificare il mio metodo per sostituire -

return (T)obj; 

con

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

Ma guardando in avanti per un modo migliore di questo cambiamento coinvolgerà casting di tipo due volte.
Qualche idea migliore?

Grazie!

+0

Che tipo di dati è columnName3 nel database? – RQDQ

+0

hai visto Linq su Dataset? –

+0

@RQDQ, è il tipo di dati 'reale' di SQL Server. Ho appena provato a replicare in app console con il seguente codice: object objValue = 7200000; float floatValue = (float) objValue; // fail here – iniki

risposta

4

Un metodo generico ha il vantaggio che è possibile ridurre un sacco di codice gonfiato, ma altrimenti sfornare il proprio involucro per ogni tipo di dati offre la flessibilità necessaria per la gestione personalizzata. E molto probabilmente le tue query db avranno un notevole effetto sulle prestazioni rispetto alla modalità di recupero.

Ti suggerisco di scrivere un set dei tuoi metodi di estensione piuttosto che avere un approccio generico. L'estensione del metodo su IDataReader offre il vantaggio di non propagare i metodi su interi sottotipi di oggetto. Ho dovuto gestire singolarmente i tipi poiché i vari connettori si comportavano in modo diverso, specialmente con il tipo Guid. Inoltre è difficile sapere se datareader legge il valore 0 o DBNull quando si restituisce 0 per entrambi i casi. Diciamo che c'è un campo enum nella tua tabella con un valore nullo. perché vorresti mai che fosse letto come il primo enum?

Basta chiamare:

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

E i metodi:

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; 
} 

E allo stesso modo,

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

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

Se si vuole veramente una funzione generica si può avere troppo.

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; 
} 

Così lo chiamano

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 

Detto questo, l'errore è perché non stai lanciando con il tipo giusto come sottolineato nei commenti. Avrei potuto passare con i controlli integrati in DBNull, ma non per evitare che i dati vengano letti più volte dal lettore, inspired from this curious case of microoptimization

0

A partire da .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); 
    } 
} 

e poi

string result = await reader.ReadAsync<string>("FieldName"); 
Problemi correlati