Domanda

Mi capita spesso di avere a che fare con DataTable connessi ai controlli griglia, l'aggiornamento personalizzato sembra sempre di produrre un sacco di codice relativo al DBNull.Value. Ho visto una domanda simile qui, ma che ci deve essere una risposta migliore:

Qual è il modo migliore per trattare con DBNull di

La cosa che trovo è 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 di nuovo 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 il codice di lavoro. Mi rendo conto che avrei potuto fare le cose come oggetti passare o ingoiare i potenziali problemi di fusione usando "come tipo" per ottenere DBUll dritto al nulla, ma entrambi questi mi sembrano nascondere le potenziali errori, mi piace la sicurezza tipo di metodo con i tipi nullable.

Esiste un metodo più pulito per fare questo tipo di sicurezza, pur mantenendo?

È stato utile?

Soluzione

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 poi essere utilizzate, 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
}

Altri suggerimenti

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

as-fusione 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 "nascondere" gli errori.

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 codice metodo statico è leggermente più pulito nel caso di scrittura, ma l'intento è meno chiaro (soprattutto nel caso di lettura).

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

che significa la stessa come

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;

(ulteriori cast oggetto è necessaria per rimuovere il tipo di ambiguità)

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 di DBNULL aiutante. L'utilizzo è semplice:

new SqlParameter("Option1", option1.ToDbNullable())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top