Question

Je dois souvent faire face à DataTables connectés aux commandes du réseau, la mise à jour personnalisée semble toujours produire beaucoup de code lié à DBNull.Value. J'ai vu une question similaire ici, mais pense qu'il doit y avoir une meilleure réponse:

Quelle est la meilleure façon de traiter avec

Ce que je trouve est que je tends à mes mises à jour de résumer la base de données dans les méthodes si je finis avec le code comme ci-dessous où je déplace le DBNull.Value à un type Nullable puis retour pour la mise à jour:

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
    }

C'était juste un exemple du flux et non le code de travail. Je me rends compte que je pouvais faire des choses comme des objets de passe ou d'avaler des problèmes de coulée potentiels à l'aide « comme type » pour obtenir DBUll directement à nulle mais ces deux me semblent cacher les erreurs potentielles, je aime la sécurité du type de la méthode avec les types nullable.

Y at-il une méthode plus propre à faire tout en maintenant la sécurité de type?

Était-ce utile?

La solution

Un couple de (très) simples méthodes génériques d'assistance peut au moins se concentrer le test en un seul morceau de code:

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

Ces méthodes peuvent ensuite être utilisées le cas échéant:

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
}

Autres conseils

Je ne vois pas quel est le problème avec as-casting et null coalescent.

as de coulée est utilisé pour la lecture:

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

null coalescent est utilisé pour l'écriture:

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

Ces deux sont des erreurs complètement typesafe et ne pas « cacher ».

Si vous voulez vraiment, vous peut envelopper ceux-ci dans des méthodes statiques, si vous vous retrouvez avec ce pour la lecture:

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

et cela pour l'écriture:

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

Le code de procédé statique est légèrement plus propre dans le cas d'écriture, mais l'intention est moins claire (en particulier dans le cas de la lecture).

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

ce qui signifie la même chose que

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

ou

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

(fonte supplémentaire à l'objet est nécessaire pour éliminer l'ambiguïté de type)

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

Ceci est ma mise en œuvre d'aide DBNULL. L'utilisation est simple:

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