Cláusula `using` anidada de Dapper - ¿Clarificación?

c# dapper

Pregunta

Estoy aprendiendo cómo Dapper está trabajando detrás de escena.

Sin embargo, vi este patrón de eliminación que no me fue entendido.

Aproximadamente en general, así es como se implementa QueryAsync :

/*1*/   public async Task<IEnumerable<T>> QueryAsync<T>(string sql, Func<IDataRecord, T> projector, DbConnection _conn, dynamic param = null)
/*2*/   {
/*3*/   
/*4*/       DbDataReader reader = null;
/*5*/       bool wasClosed = _conn.State == ConnectionState.Closed;
/*6*/       try
/*7*/       {
/*8*/   
/*9*/           using (var cmd = _conn.CreateCommand())
/*10*/          {
/*11*/          if (param!=null)
/*12*/              foreach (var prop in param.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
/*13*/              {
/*14*/                  var parameter = cmd.CreateParameter();
/*15*/                  parameter.ParameterName = prop.Name;
/*16*/                  parameter.Value = prop.GetValue(param, null);
/*17*/                  cmd.Parameters.Add(parameter);
/*18*/              }
/*19*/   
/*20*/              await _conn.OpenAsync().ConfigureAwait(false);
/*21*/              cmd.CommandTimeout = 100000;
/*22*/              cmd.CommandText = sql;
/*23*/              reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
/*24*/              List<T> buffer = new List<T>();
/*25*/              while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
/*26*/              return buffer;
/*27*/          }
/*28*/   
/*29*/      }
/*30*/      finally
/*31*/      {
/*32*/          using (reader) { }
/*33*/          if (wasClosed) _conn.Close();
/*34*/      }
/*35*/   }

Puedo entender por qué no usó el using la conexión, porque quería cerrar la conexión condicionalmente a través de la variable wasClosed .
Para hacerlo, debe usar la cláusula try/finally . (por lo que el cierre condicional estará en la cláusula finally )

Pero mi pregunta es sobre la línea # 32.

En lugar de usar en la cláusula finally , podría hacer:

using (DbDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
    List<T> buffer = new List<T>();
    while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
    return buffer;
}

Por lo que el finally cláusula se queda con:

finally
{
    //using (reader) { } //removed
    if (wasClosed) _conn.Close();
}

Pregunta

He visto esta cláusula de using en una cláusula finally muchas veces en dapper.

Debo perderme algo aquí, pero ¿qué logra este patrón que mi sugerencia no tiene?

Respuesta aceptada

No soy @MarcGravell, pero creo que hay una cosa que te estás perdiendo. El código que pegó no coincide exactamente con el enlace al que hace referencia. La ruta del código relevante tiene este aspecto:

try
{
     if (command.Buffered)
     {
         List<T> buffer = new List<T>();
         while (await reader.ReadAsync(cancel).ConfigureAwait(false))
         {
             buffer.Add((T)func(reader));
         }
         while (await reader.NextResultAsync().ConfigureAwait(false)) { }
         command.OnCompleted();
         return buffer;
     }
     else
     {
         // can't use ReadAsync / cancellation; but this will have to do
         wasClosed = false; // don't close if handing back an open reader; 
                            // rely on the command-behavior.

         var deferred = ExecuteReaderSync<T>(reader, func, command.Parameters);
         reader = null; // to prevent it being disposed before the caller gets to see it
         return deferred;
     }
}
finally
{
    using (reader) { } // dispose if non-null
    if (wasClosed) cnn.Close();
}

El método puede devolver un resultado almacenado (indicado por el command.Buffered búfer) o un iterador diferido. Si Marc iba a envolver el lector con una instrucción de using y devolver un iterador, este (el lector) se eliminaría cuando el sitio de llamada lo ejecutara. Al configurar el lector como null (en la línea antes de devolver el resultado diferido) evita que el lector sea eliminado, porque el uso en el bloque finally se traduciría a esto:

finally
{
    IDisposable disposable = reader;
    try
    {
    }
    finally
    {
        if (dispoable != null)
        {
            disposable.Dispose();
        }
    }
}

Cuando establece que el lector es null , no está dispuesto, y la referencia existe en el iterador sigue viva, apuntando al lector. De esta forma, puede disponer el lector en la ruta de código normal, pero mantenerlo activo si se solicita un iterador diferido.



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é