2008-09-06 8 views
15

Aquí hay un código de ejemplo para recuperar datos de una base de datos utilizando la palabra clave de rendimiento que he encontrado en algunos lugar mientras alrededor de google:¿Usar el rendimiento para iterar sobre un lector de datos podría no cerrar la conexión?

public IEnumerable<object> ExecuteSelect(string commandText) 
{ 
    using (IDbConnection connection = CreateConnection()) 
    { 
     using (IDbCommand cmd = CreateCommand(commandText, connection)) 
     { 
      connection.Open(); 
      using (IDbDataReader reader = cmd.ExecuteReader()) 
      { 
       while(reader.Read()) 
       { 
        yield return reader["SomeField"]; 
       } 
      } 
      connection.Close(); 
     } 
    } 
} 

Estoy en lo correcto al pensar que en este código de ejemplo, la conexión no lo haría cerrarse si no iteramos sobre todo el lector de datos?

Aquí es un ejemplo de que no se cierre la conexión, si entiendo correctamente dió ..

foreach(object obj in ExecuteSelect(commandText)) 
{ 
    break; 
} 

Para una conexión db que podría no ser catastrófica, supongo que el GC sería limpiarlo con el tiempo, pero ¿Qué pasaría si en lugar de una conexión fuera un recurso más crítico?

Respuesta

11

El iterador que sintetiza el compilador implementa IDisposable, que las llamadas foreach cuando el bucle foreach se sale.

El método Iterator's Dispose() limpiará las instrucciones de uso en la salida anticipada.

Siempre que utilice el iterador en un bucle foreach, utilizando() bloquee, o llame al método Dispose() de alguna otra manera, se realizará la limpieza del iterador.

2

La conexión se cerrará automáticamente ya que la está utilizando dentro del bloque "using".

0

A juzgar por this technical explanation, su código no funcionará como se esperaba, pero abortará en el segundo elemento, porque la conexión ya estaba cerrada al devolver el primer elemento.

@Joel Gauvreau: Sí, debería haber leído. Part 3 de esta serie explica que el compilador agrega un manejo especial para que finalmente los bloques se disparen solo en el real final.

2

A partir de la prueba simple que he probado, aku está en lo correcto, se llama a desechar tan pronto como la salida del bloque foreach.

@David: Sin embargo, la pila de llamadas se mantiene entre llamadas, por lo que la conexión no se cerraría porque en la próxima llamada volveríamos a la siguiente instrucción después del rendimiento, que es el bloque while.

Tengo entendido que cuando se elimina el iterador, la conexión también se eliminará con él. También creo que Connection.Close no sería necesario porque se trataría cuando el objeto se elimine debido a la cláusula de uso.

Aquí es un programa simple que traté de probar el comportamiento ...

class Program 
{ 
    static void Main(string[] args) 
    { 
     foreach (int v in getValues()) 
     { 
      Console.WriteLine(v); 
     } 
     Console.ReadKey(); 

     foreach (int v in getValues()) 
     { 
      Console.WriteLine(v); 
      break; 
     } 
     Console.ReadKey(); 
    } 

    public static IEnumerable<int> getValues() 
    { 
     using (TestDisposable t = new TestDisposable()) 
     { 
      for(int i = 0; i<10; i++) 
       yield return t.GetValue(); 
     } 
    } 
} 

public class TestDisposable : IDisposable 
{ 
    private int value; 

    public void Dispose() 
    { 
     Console.WriteLine("Disposed"); 
    } 

    public int GetValue() 
    { 
     value += 1; 
     return value; 
    } 
} 
Cuestiones relacionadas