Valeur de retour nullable sur un argument générique

c# dapper generics nullable

Question

J'essaie d'enregistrer un modèle dans la base de données à l'aide de Dapper. Je configure les paramètres avec un paramètre d'entrée / sortie qui est un int avec une valeur de clé primaire existante utilisée pour une mise à jour.

public async Task<TKey> SaveAsync<TKey>(IGraph builder, IDataContext context = null)
{
    var parameters = this.GetParametersFromDefinition(builder, DefinitionDirection.In);

    // See if we have a key defined. If not, we assume this is a new insert.
    // Otherwise we pull the key out and assume it's an update.
    PropertyDefinition key = builder.GetKey();

    if (key != null)
    {
        parameters.Add(key.ResolvedName, key.PropertyValue, null, ParameterDirection.InputOutput);
    }
    else
    {
        throw new InvalidOperationException("The data graph did not have a primary key defined for it.");
    }

    await this.ExecuteProcedure(parameters, builder, context);

    object returnedId = parameters.Get<TKey>(key.ResolvedName);
    return returnedId == null ? default(TKey) : (TKey)returnedId;
}

private Task ExecuteProcedure(DynamicParameters parameters, IGraph builder, IDataContext context = null)
{
    ProcedureDefinition mapping = builder.GetProcedureForOperation(ProcedureOperationType.Insert);
    if (string.IsNullOrEmpty(mapping.StoredProcedure))
    {
        throw new InvalidOperationException("No stored procedure mapped to the builder.");
    }

    // Query the database
    return this.SetupConnection(
        context,
        (connection, transaction) => connection.ExecuteAsync(
            mapping.StoredProcedure,
            parameters,
            commandType: CommandType.StoredProcedure,
            transaction: transaction));
}

Il est invoqué comme ceci:

this.Address.AddressId = await repository.SaveAsync<int>(graph);

Lorsque j'évalue les paramètres, je vois mes paramètres d'entrée / sortie.

Paramètres dynamiques

Cependant, lorsque j'essaie d'exécuter cette ligne dans ma sauvegarde:

TKey returnedId = parameters.Get<TKey>(key.ResolvedName);

On me donne l'exception suivante:

Exception: Caught: "Tentative de conversion d'un DBNull en un type non nullable! Notez que les paramètres out / return n'auront pas de valeurs mises à jour tant que le flux de données ne sera pas terminé (après le foreach pour Query (..., buffered: false)) ou après l'élimination de GridReader pour QueryMultiple) "(System.ApplicationException) Une exception System.ApplicationException a été interceptée:" Tentative de conversion d'un DBNull en un type non nullable! Notez que les paramètres out / return n'auront pas de valeur mise à jour tant que le flux de données complète (après le 'foreach' pour Query (..., tamponné: false) ou après que GridReader a été supprimé pour QueryMultiple) "Heure: 21/07/2015 22h19: 48 Thread: [7200]

Je suppose que c'est un problème avec le type générique qui n'est pas nullable dans ce cas, car c'est un entier. Est-ce parce que Dapper renvoie toujours un nullable? Je viens d'affecter la valeur OUTPUT de la procédure stockée à une valeur constante pour que quelque chose soit affecté à la sortie.

Comment puis-je contourner le retour d'une valeur nullable, est-ce la seule façon de contourner cela en passant dans int? comme type générique?

Mettre à jour

J'ai été capable de le résoudre en utilisant cette approche. SO ne me laissera pas le poster comme une réponse pour le moment. Lorsque le délai est écoulé, je posterai une réponse, sauf si quelqu'un d'autre a de meilleures idées.

object returnedId = parameters.Get<TKey>(key.ResolvedName);
if (returnedId == null)
{
    return default(TKey);
}

return (TKey)returnedId;

Réponse acceptée

Si DBNull est une valeur valide et doit signifier autre chose que la valeur par défaut (TKey) (ex: default (int) = 0), et si TKey sera toujours un type de valeur, contraignez TKey en tant que struct comme suit:

public async Task<TKey?> SaveAsync<TKey>(IGraph builder, IDataContext context = null) where TKey : struct
{
    ...
}

Ensuite, obtenez la clé comme ça:

TKey? returnedId = parameters.Get<TKey?>(key.ResolvedName);

Le type de retour de SaveAsync indiquera que la clé peut être nulle. Si la clé ne doit jamais être NULL et si DBNull doit être défini par défaut sur TKey, utilisez simplement ce qui suit:

public async Task<TKey?> SaveAsync<TKey>(IGraph builder, IDataContext context = null) where TKey : struct
{
    ...
    return parameters.Get<TKey?>(key.ResolvedName).GetValueOrDefault();
}


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi