¿Es posible que el uso de rendimiento para iterar sobre un lector de datos no cierre la conexión?

StackOverflow https://stackoverflow.com/questions/47521

  •  09-06-2019
  •  | 
  •  

Pregunta

Aquí hay un código de muestra para recuperar datos de una base de datos usando la palabra clave de rendimiento que encontré en algunos lugares mientras buscaba en Google:

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 cierto al pensar que en este código de muestra, la conexión no se cerraría si no iteramos sobre todo el lector de datos?

Aquí hay un ejemplo que no cerraría la conexión, si entiendo el rendimiento correctamente.

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

Para una conexión de base de datos que podría no ser catastrófica, supongo que el GC eventualmente la limpiaría, pero ¿y si en lugar de una conexión fuera un recurso más crítico?

¿Fue útil?

Solución

El iterador que sintetiza el compilador implementa IDisposable, que llama a foreach cuando se sale del bucle foreach.

El método Dispose() del iterador limpiará las declaraciones de uso en la salida anticipada.

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

Otros consejos

La conexión se cerrará automáticamente ya que la estás usando dentro del bloque "usar".

Según la prueba simple que he probado, aku tiene razón, se llama a disponer tan pronto como sale el bloque foreach.

@David :Sin embargo, la pila de llamadas se mantiene entre llamadas, por lo que la conexión no se cerrará porque en la siguiente llamada regresaremos 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 solucionaría cuando el objeto se elimine debido a la cláusula de uso.

Aquí hay un programa simple que probé para 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;
    }
}

A juzgar por esta explicación técnica, su código no funcionará como se esperaba, sino que se cancelará en el segundo elemento, porque la conexión ya se cerró cuando se devolvió el primer elemento.

@Joel Gauvreau:Sí, debería haber seguido leyendo. parte 3 de esta serie explica que el compilador agrega un manejo especial para que los bloques finalmente se activen solo en el real fin.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top