Dapper вложенное предложение `using` - Разъяснение?

Я изучаю, как Dapper работает за кулисами.

Однако я видел эту схему распоряжения, которая мне не понятна.

Грубо вообще - это, как реализовано 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*/   }

Я понимаю, почему он не использовал using по соединению, потому что он хотел условно закрыть соединение с помощью wasClosed переменная.
Для этого он должен использовать предложение try/finally. (поэтому условное закрытие будет в предложении finally)

Но мой вопрос касается строки # 32.

Вместо используя в предложении finally, он мог бы сделать:

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

Итак, предложение finally осталось:

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

Вопрос

Я видел это предложение using в предложении finally много раз в dapper.

Мне, должно быть, что-то не хватает, но что делает эта модель, что мое предложение не делает?

Ответы

Ответ 1

Я не @MarcGravell, но я думаю, что есть одна вещь, которую вам не хватает. Код, который вы вставили, точно не соответствует ссылке, которую вы ссылаетесь. Соответствующий путь кода выглядит следующим образом:

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

Метод может либо возвращать буферный результат (указанный флагом command.Buffered), либо отложенный итератор. Если Marc должен был обернуть читателя оператором using и вернуть итератор, он (читатель) был бы удален к тому моменту, когда сайт-сайт выполнил его. Установив читателя на null (в строке до того, как он вернет отложенный результат), он не позволяет читателю быть удаленным, потому что использование в блоке finally будет переведено на это:

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

Когда он устанавливает читателя в null, он не удаляется, и ссылка существует в итераторе, все еще жива, указывая на читателя. Таким образом, он может обедать читателю в обычной кодировке, но сохранить его в живых, если был запрошен отложенный итератор.