2010-08-17 17 views
10

Con frecuencia tengo que lidiar con DataTables conectados a controles de cuadrícula, la actualización personalizada siempre parece producir una gran cantidad de código relacionado con DBNull.Value. Vi a una pregunta similar aquí, pero creo que debe haber una respuesta mejor:Tratando con DBNull.Value

What is the best way to deal with DBNull's

Lo que me parece es que tiendo a encapsular mis actualizaciones de la base de métodos, así que termino con un código como por debajo de donde muevo el DBNull.Value a un tipo anulable y luego de vuelta para la actualización:

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 
    } 

Eso fue sólo un ejemplo del flujo y el código no funciona. Me doy cuenta de que podría hacer cosas como pasar objetos o tragar posibles problemas de conversión usando "como tipo" para obtener DBUll directo a nulo, pero ambos me parecen ocultar posibles errores, me gusta el tipo de seguridad del método con tipos que aceptan nulos.

¿Existe un método más limpio para hacer esto mientras se mantiene el tipo de seguridad?

+0

¿Por qué no utiliza DataRow fuertemente tipado directamente? Mencionaste que debes usar DataTables. Si esas tablas de datos están fuertemente tipadas, puede enviar una fila de datos a su método. La fila de datos ya usa DBNull. –

Respuesta

14

Un par de métodos (muy) simples auxiliares genéricos al menos podría concentrar la prueba en una pieza de código:

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

Estos métodos se pueden utilizar en su 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

Recomiende usar 'Convert.IsDBNull()' en lugar de 'value == DBNull.Value'. Y 'FromDBNull' /' ToDBNull' en lugar de simplemente FromDB'/'ToDB', probablemente. – abatishchev

+0

Esto se ve más ordenado, gracias. Pensé que los genéricos podrían ser el camino a seguir; lo usaré a menos que a nadie se le ocurra una mejor solución. – PeteT

+1

@abatishchev: Sí, usar 'Convert.IsDBNull' probablemente sería más elegante; encapsulando la prueba que en sí misma no es exactamente el propósito del método. Con respecto a la nomenclatura puedo aceptar que 'FromDB' y' ToDB' son un poco cortos, aunque preferiría llamar a los métodos 'ToDBValue' y' FromDBValue' ya que no convierten todos los valores a 'DBNull', pero más bien devuelve el valor pasado en una forma que se puede utilizar al enviarlo o recibirlo de la base de datos. –

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

que significa lo mismo que

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; 

(se requiere fundido adicional para oponerse a eliminar la ambigüedad tipo)

+0

@ petebob796: Mi solución es la mayor marea, ¿no? – abatishchev

1

no veo qué tiene de malo as -casting y null coalescencia.

as -casting se utiliza para la lectura:

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

null coalescencia se utiliza para la escritura:

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

Ambos son completamente typesafe ni causen errores "ocultar".

Si realmente desea, puede envolver éstos en los métodos estáticos, por lo que acabar con esto por la lectura:

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

y esto para la escritura:

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

El método estático el código es un poco más limpio en el caso de escritura, pero la intención es menos clara (especialmente en el caso de lectura).

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

Esta es mi implementación de DBNULL helper.El uso es simple:

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