2015-07-04 3 views
14

Sto imparando come Dapper sta lavorando dietro le quinte.Clausola `using` annidata di Dapper - Chiarimento?

Tuttavia ho visto questo schema di smaltimento che non mi è compreso.

Circa in generale -this è come QueryAsync è implementato:

/*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*/ } 

posso capire perché non ha usato using tramite la connessione, è perché voleva condizionale chiudere la connessione via la variabile wasClosed.
Per farlo - deve utilizzare la clausola try/finally. (quindi la chiusura condizionale sarà nella clausola finally)

Ma la mia domanda riguarda la linea n. 32.

Invece di fare using at the finally clause, che poteva fare:

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

Quindi la clausola di finally è lasciato con:

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

Domanda

ho visto questa clausola using in una clausola definitiva molte volte in dapper.

Mi manca qualcosa qui, ma cosa fa questo schema che il mio suggerimento non ha?

+2

Questo sembra un difetto. La tua proposta è migliore. Probabilmente non c'è una buona ragione per questo. – usr

risposta

7

Non sono @MarcGravell, ma penso che ci sia una cosa che ti manca. Il codice incollato non corrisponde esattamente al link a cui fai riferimento. Il percorso relativo codice simile a questo:

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

Il metodo può restituire un risultato tamponata (indicato dal flag command.Buffered) o un iteratore differito. Se Marc dovesse avvolgere il lettore con un'istruzione using e restituire un iteratore, esso (il lettore) sarebbe stato disposto al momento dell'esecuzione del sito di chiamata. Impostando il lettore a null (in linea prima che restituisce il risultato differita) che impedisce al lettore di essere disposta, perché l'utilizzo nel blocco finally sarebbe tradotto a questo:

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

Quando imposta il lettore a null, non è disposto, e il riferimento esiste nell'iteratore è ancora attivo, che punta al lettore. In questo modo, può sia disporre il lettore nel normale codepath, ma tenerlo in vita se è stato richiesto un iteratore differito.

+0

Bella cattura. A proposito, sai perché scrive _ "non posso usare ReadAsync" _? Oltre al token di cancellazione, cos'altro può succedere? Non sono sicuro di aver capito il ruolo del buffer qui –

+0

@RoyiNamir Penso che * non possa usare 'ReadAsync' * per il fatto che non può restituire un iteratore asincrono. Non puoi 'yield return 'un'operazione asincrona. –