Usar Dapper QueryAsync para devolver un solo objeto

asynchronous c# dapper repository

Pregunta

Desafortunadamente, nuestra base de datos data de los años 90. Su legado es tan fuerte que todavía estamos usando SP para hacer la mayoría de las operaciones CRUD. Sin embargo, parece que Dapper se adapta bastante bien y acabamos de empezar a jugar.

Sin embargo, me preocupa un poco cómo manejar una sola fila de datos. En este caso, estoy usando QueryAsync para llamar al SP que pasa una identificación. Como puede ver, el objeto está volviendo fuera de la llamada asincrónica (*).

¿Voy a estar en problemas? Si es así, ¿alguien sabe cómo manejarlo? ¿Debo usar un QuerySync en su lugar?

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

(*) El objeto SchemePolicy devuelto por FirstOfDefault () no es un método asíncrono.

Respuesta aceptada

En primer lugar, no creo que necesite la null check , Dapper devolverá una fila cero para una consulta. TENGA EN CUENTA que esto es VERDADERO para SQL Server pero debe ser el mismo para cualquier otro RDBMS. Así que esto

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

se puede simplemente escribir como

return schemePolicy.FirstOrDefault();

Ahora para abordar la verdadera preocupación, y usted mencionó:

el objeto está volviendo fuera de la llamada asincrónica (*)

Eso no es verdad. Si lo escribe de cualquier manera, SÓLO obtendrá su objeto después de que se haya ejecutado la consulta. Entonces, el siguiente conjunto de códigos dará el mismo comportamiento:

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

y

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

La preocupación ahora es realmente con la forma en que llama a GetById para asegurarse de que (1) el método no bloqueará ningún otro hilo y (2) que obtendrá su objeto de destino SÓLO cuando la consulta haya terminado de ejecutarse. Aquí hay un fragmento para una aplicación de consola con la que puedes probarlo:

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

Esa prueba le mostrará que GetValue que en consecuencia llama al método GetById no bloquea el resto del código. Además, nada se devuelve desde FirstOrDefault hasta que la consulta se haya procesado.

Aquí está el código de soporte para la consulta en caso de que alguien quiera probar y verificar que el concepto sea válido (el código funciona con SQL Server 2008 y posterior):

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué