¿Hay algún inconveniente al utilizar un IEnumerable < T > tipo de retorno para los datos de SQL?

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

  •  05-07-2019
  •  | 
  •  

Pregunta

Mi pregunta está relacionada con el estado de la conexión SQL, la carga, etc. según el siguiente código:

public IEnumberable<MyType> GetMyTypeObjects()
{
  string cmdTxt = "select * from MyObjectTable";

  using(SqlConnection conn = new SqlConnection(connString))
  {
    using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
    {
      conn.Open();
      using(SqlDataReader reader = cmd.ExecuteReader())
      {
         while(reader.Read())
         {
            yield return Mapper.MapTo<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

Puedo ver que esto puede ser un problema si hay muchos procesos que ejecutan código similar con largos tiempos de ejecución entre iteraciones del objeto IEnumerable, porque las conexiones se abrirán por más tiempo, etc. Sin embargo, también parece plausible que esto se reduzca. el uso de la CPU en el servidor SQL porque solo devuelve datos cuando se utiliza el objeto IEnumerable. También reduce el uso de memoria en el cliente porque el cliente solo tiene que cargar una instancia de MyType mientras funciona, en lugar de cargar todas las apariciones de MyType (mediante la iteración de todo el DataReader y la devolución de una lista o algo así).

  • ¿Hay alguna instancia en la que pueda pensar en dónde no querría usar IEnumerable de esta manera, o alguna instancia en la que considere que se ajusta perfectamente?

  • ¿Qué tipo de carga coloca esto en el servidor SQL?

  • ¿Es esto algo que usarías en tu propio código (a menos que se mencione NHibernate, Subsonic, etc.)?

  •  -
¿Fue útil?

Solución

No lo usaría, porque oculta lo que está sucediendo y posiblemente podría dejar las conexiones de la base de datos sin la eliminación adecuada.

El objeto de conexión se cerrará cuando haya leído más allá del último registro y, si deja de leer antes, el objeto de conexión no se eliminará. Si, por ejemplo, sabe que siempre tiene diez registros en un resultado y solo tiene un bucle que lee esos diez registros del enumerador sin hacer la undécima llamada de lectura que lee más allá del último elemento, la conexión no se cierra correctamente. Además, si desea utilizar solo una parte del resultado, no tiene forma de cerrar la conexión sin leer el resto de los registros.

Incluso las extensiones incorporadas para los enumeradores pueden causar esto incluso si las usas correctamente:

foreach (MyType item in GetMyTypeObjects().Take(10)) {
   ...
}

Otros consejos

Este no es un patrón que seguiría. No me preocuparía tanto la carga en el servidor como lo haría con los bloqueos. Seguir este patrón integra el proceso de recuperación de datos en el flujo de la lógica de su negocio, y eso parece una receta completa para los problemas; no tienes idea de lo que sucede en el lado de la iteración y te estás insertando en él. Recupere sus datos de una sola vez, luego permita que el código del cliente se enumere sobre ellos una vez que haya cerrado el lector.

Recomiendo contra la optimización previa. En muchas situaciones, las conexiones se agruparán.

Tampoco espero ninguna diferencia en la carga de SQL Server: la consulta ya se habrá compilado y se estará ejecutando.

Mi preocupación sería que te pongas completamente a merced del código del cliente.

Ya mencionó que el código de llamada podría mantener la conexión abierta más tiempo del estrictamente necesario, pero también existe la posibilidad de que nunca permita que los objetos se cierren / eliminen.

Mientras el código del cliente use foreach , use , etc. o llame explícitamente al método Dispose del enumerador, entonces está bien, pero hay nada que lo detenga haciendo algo como esto:

var e = GetMyTypeObjects().GetEnumerator();
e.MoveNext();    // open the connection etc

// forget about the enumerator and go away and do something else
// now the reader, command and connection won't be closed/disposed
// until the GC kicks in and calls their finalisers
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top