web-dev-qa-db-fra.com

Utilisation de Dapper QueryAsync pour renvoyer un seul objet

Malheureusement, notre DB date des années 90. Son héritage est si fort que nous utilisons toujours SP pour effectuer la plupart des opérations CRUD. Cependant, il semble que Dapper convient plutôt bien et nous venons juste de commencer à jouer avec.

Cependant, je suis un peu préoccupé par la façon de gérer une seule ligne de données. Dans ce cas, j'utilise QueryAsync pour appeler le SP en passant un ID. Comme vous pouvez le voir, l'objet retourne en dehors du appel asynchrone (*).

Vais-je avoir des ennuis? Si oui, quelqu'un sait-il comment gérer cela? Dois-je plutôt utiliser un QuerySync?

public class SchemePolicyRepository : ISchemePolicyRepository
{
    private readonly SqlConnection sql;

    protected SchemePolicyRepository(SqlConnection connection)
    {
        sql = connection;
    }
    ... 
    public async Task<SchemePolicy> GetById(string id)
    {
        var schemePolicy = await sql.QueryAsync<SchemePolicy>("risk.iE_GetSchemePolicyById",
            new { Id = id },
            commandType: CommandType.StoredProcedure);
        return schemePolicy != null ? schemePolicy.FirstOrDefault() : null;
    }
    ...
}

(*) L'objet SchemePolicy renvoyé par FirstOfDefault () n'est pas une méthode asynchrone.

11
Stefano Magistri

Tout d'abord, je ne pense pas que vous ayez besoin du null check, Dapper renverra zéro ligne pour une requête. PRENEZ NOTE que c'est VRAI pour SQL Servermais devrait être le même pour tout autre SGBDR. Donc ça

return schemePolicy != null ? schemePolicy.FirstOrDefault() : null;

peut être simplement écrit comme

return schemePolicy.FirstOrDefault();

Maintenant, pour aborder la vraie préoccupation, et vous avez mentionné:

l'objet retourne en dehors de l'appel asynchrone (*)

Ce n'est pas vrai. Si vous l'écrivez de toute façon, vous SEULEMENT récupérerez votre objet après l'exécution de la requête. Ainsi, les deux codes suivants donneront le même comportement:

var schemePolicy = await sql.QueryAsync<SchemePolicy>("sp", {rest of code});
return schemePolicy.FirstOrDefault();

et

var schemePolicy = sql.QueryAsync<SchemePolicy>("sp", {rest of code});
return schemePolicy.Result.FirstOrDefault();

Le problème est maintenant avec la façon dont vous appelez GetById pour vous assurer que (1) la méthode ne bloquera aucun autre thread et (2) que vous obtiendrez votre objet cible UNIQUEMENT lorsque la requête sera terminée. Voici un extrait d'une application console avec laquelle vous pouvez la tester:

static async void GetValue()
{
    var repo = new SchemePolicyRepository(new DbManager()); // creates an open connection 
    var result = await repo.GetById();
    Console.WriteLine(result);
}

static void Main(string[] args)
{
    GetValue();   
    Console.WriteLine("Query is running...");
    Console.ReadKey();
}

Ce test vous montrera que GetValue qui appelle par conséquent la méthode GetById ne bloque pas le reste du code. De plus, rien n'est renvoyé par FirstOrDefault tant que la requête n'a pas été traitée.

Voici le code de prise en charge de la requête au cas où quelqu'un voudrait essayer de vérifier que le concept est valide (le code fonctionne avec SQL Server 2008 et versions ultérieures):

public async Task<int> GetById()
{
    var sql = @"
WAITFOR DELAY '00:00:05';
select 1 where 1=1";

    var result = await {the_open_connection}.QueryAsync<int>(sql);    
    return result.FirstOrDefault();
}
11
von v.

Dapper prend en charge QueryFirstOrDefaultAsync() de nos jours, vous pouvez donc écrire le code comme ceci,

public async Task<SchemePolicy> GetById(string id)
{
    return await sql.QueryFirstOrDefaultAsync<SchemePolicy>("risk.iE_GetSchemePolicyById",
        new { Id = id },
        commandType: CommandType.StoredProcedure);
}
6