2010-08-17 14 views
10

Spesso ho a che fare con DataTables connessi ai controlli della griglia, l'aggiornamento personalizzato sembra sempre produrre un sacco di codice relativo a DBNull.Value. Ho visto una domanda simile qui, ma che ci deve essere una risposta migliore:Gestione DBNull.Value

What is the best way to deal with DBNull's

La cosa che ho trovato è tendo a incapsulare i miei aggiornamenti del database in modalità così finisco con il codice come sotto dove mi muovo il DBNull.Value a un tipo nullable e poi indietro per l'aggiornamento:

private void UpdateRowEventHandler(object sender, EventArgs e) 
{ 
    Boolean? requiresSupport = null; 
    if (grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport) != DBNull.Value) 
     requiresSupport = (bool)grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport); 

    AdditionalSupport.UpdateASRecord(year, studentID, requiresSupport) 
} 

internal static void UpdateASRecord(
     string year, 
     string studentID,    
     bool? requiresSupport) 
    { 
     List<SqlParameter> parameters = new List<SqlParameter>(); 

     parameters.Add(new SqlParameter("@year", SqlDbType.Char, 4) { Value = year }); 
     parameters.Add(new SqlParameter("@student_id", SqlDbType.Char, 11) { Value = studentID }); 

     if (requiresSupport == null) 
      parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) { Value = DBNull.Value }); 
     else 
      parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) { Value = requiresSupport }); 

     //execute sql query here to do update 
    } 

Questo è stato solo un esempio del flusso e non codice di lavoro. Mi rendo conto che potrei fare cose come passare oggetti o inghiottire potenziali problemi di lancio usando "come tipo" per ottenere DBUll direttamente a null ma entrambi sembrano nascondere potenziali errori, mi piace il tipo safety del metodo con tipi nullable.

Esiste un metodo più pulito per eseguire questa operazione mantenendo la sicurezza del tipo?

+0

Perché non utilizzare direttamente DataRow fortemente digitato? Hai detto che devi usare DataTable. Se queste tabelle di dati sono fortemente digitate, puoi inviare una riga di dati al tuo metodo. La riga dati utilizza già DBNull. –

risposta

14

Un paio di (molto) semplici metodi di supporto generici potrebbe almeno concentrare il test in un unico pezzo di codice:

static T FromDB<T>(object value) 
{ 
    return value == DBNull.Value ? default(T) : (T)value; 
} 

static object ToDB<T>(T value) 
{ 
    return value == null ? (object) DBNull.Value : value; 
} 

Questi metodi possono essere utilizzati, se del caso:

private void UpdateRowEventHandler(object sender, EventArgs e) 
{ 
    AdditionalSupport.UpdateASRecord(year, studentID, 
     FromDB<Boolean?>(grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport))); 
} 

internal static void UpdateASRecord(
     string year, 
     string studentID, 
     bool? requiresSupport) 
{ 
    List<SqlParameter> parameters = new List<SqlParameter>(); 

    parameters.Add(new SqlParameter("@year", SqlDbType.Char, 4) { Value = year }); 
    parameters.Add(new SqlParameter("@student_id", SqlDbType.Char, 11) { Value = studentID }); 
    parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) { Value = ToDB(requiresSupport) }); 

    //execute sql query here to do update 
} 
+2

Consiglia di usare 'Convert.IsDBNull()' invece di 'value == DBNull.Value'. E 'FromDBNull' /' ToDBNull' piuttosto che solo FromDB'/'ToDB', probabilmente. – abatishchev

+0

Questo sembra un po 'più ordinato, pensavo che i generici potrebbero essere la strada da percorrere. Userò questo a meno che qualcuno non trovi una soluzione migliore. – PeteT

+1

@abatishchev: Sì, usare 'Convert.IsDBNull' sarebbe probabilmente più elegante; incapsulare il test che di per sé non è lo scopo del metodo. Per quanto riguarda la denominazione, posso convenire che 'FromDB' e' ToDB' sono un po 'brevi, anche se preferisco chiamare i metodi 'ToDBValue' e' FromDBValue' poiché non convertono tutti i valori in 'DBNull', ma piuttosto restituisce il valore passato in un modulo che è utilizzabile quando lo invia o riceve dal DB. –

0
parameters.Add("@requires_support", SqlDbType.Bit).Value = (object)requiresSupport ?? DBNull.Value; 

che equivale allo stesso

parameters.Add("@requires_support", SqlDbType.Bit).Value = (requiresSupport != null) ? (object)requiresSupport : DBNull.Value; 

o

if (requiresSupport != null) 
    parameters.Add("@requires_support", SqlDbType.Bit).Value = requiresSupport 
else 
    parameters.Add("@requires_support", SqlDbType.Bit).Value = DBNull.Value; 

(getto aggiuntivo di opporsi è necessario per rimuovere il tipo di ambiguità)

+0

@ petebob796: La mia soluzione è la più alta, no? – abatishchev

1

non vedo cosa c'è di sbagliato con as -casting e null coalescenza.

as -casting viene utilizzato per la lettura:

bool? requiresSupport = 
    grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport) as bool?; 
AdditionalSupport.UpdateASRecord(year, studentID, requiresSupport); 

null coalescenza viene utilizzato per la scrittura:

parameters.Add(new SqlParameter("@student_id", SqlDbType.Char, 11) 
    { Value = studentID }); 
parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) 
    { Value = (object)requiresSupport ?? DBNull.Value }); 

Entrambi questi sono completamente typesafe e non fare errori "nascondere".

Se si vuole veramente, è può avvolgere queste in metodi statici, così si finisce con questo per la lettura:

//bool? requiresSupport = 
// grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport) as bool?; 
bool? requiresSupport = FromDBValue<bool?>(
    grdMainLevel1.GetFocusedRowCellValue(colASRequiresSupport)); 

e questo per la scrittura:

//parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) 
// { Value = (object)requiresSupport ?? DBNull.Value }); 
parameters.Add(new SqlParameter("@requires_support", SqlDbType.Bit) 
    { Value = ToDBValue(requiresSupport) }); 

Il metodo statico il codice è leggermente più pulito nel caso di scrittura, ma l'intento è meno chiaro (specialmente nel caso di lettura).

0
public static object DbNullable<T>(T? value) where T : struct 
{ 
    if (value.HasValue) 
    { 
     return value.Value; 
    } 
    return DBNull.Value; 
} 

public static object ToDbNullable<T>(this T? value) where T : struct 
{ 
    return DbNullable(value); 
} 

Questa è la mia implementazione dell'helper DBNULL.L'utilizzo è semplice:

new SqlParameter("Option1", option1.ToDbNullable())