Desafortunadamente, nuestra base de datos se remonta a los años 90. Su legado es tan fuerte que todavía usamos SP para realizar la mayoría de las operaciones CRUD. Sin embargo, parece que Dapper encaja bastante bien y acabamos de empezar a jugar con él.
Sin embargo, me preocupa un poco cómo manejar una sola fila de datos. En este caso, estoy usando QueryAsync para llamar al SP pasando una ID. Como puede ver, el objeto regresa fuera de la llamada asíncrona (*).
¿Voy a estar en problemas? Si es así, ¿alguien sabe cómo manejarlo? ¿Necesito 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.
Dapper es compatible con QueryFirstOrDefaultAsync() hoy en día, por lo que podría escribir el código de esta manera,
public async Task<SchemePolicy> GetById(string id) { return await sql.QueryFirstOrDefaultAsync<SchemePolicy>("risk.iE_GetSchemePolicyById", new { Id = id }, commandType: CommandType.StoredProcedure); }En primer lugar, no creo que necesite la null check , Dapper devolverá la fila cero para una consulta. TENGA EN CUENTA que esto es VERDADERO para SQL Server , pero debería ser lo mismo para cualquier otro RDBMS. Así que esto
return schemePolicy != null ? schemePolicy.FirstOrDefault() : null;puede escribirse simplemente como
return schemePolicy.FirstOrDefault();Ahora para abordar la preocupación real, y usted mencionó:
el objeto regresa fuera de la llamada asíncrona (*)
Eso no es verdad. Si lo escribe de cualquier manera, SOLO obtendrá su objeto después de que se haya ejecutado la consulta. Entonces, los siguientes dos conjuntos de códigos producirán 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 la forma en que llama a GetById para asegurarse de que (1) el método no bloquee ningún otro subproceso y (2) que obtendrá su objeto de destino SOLO cuando la consulta haya terminado de ejecutarse. Aquí hay un fragmento de una aplicación de consola con la que puede probarla:
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, que FirstOrDefault no devuelve nada hasta que se haya procesado la consulta.
Aquí está el código de soporte para la consulta en caso de que alguien quiera probar y verificar que el concepto es válido (el código funciona con SQL Server 2008 y versiones posteriores):
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(); }