EF 6: Buffering versus Streaming

Hace poco se incorporó un change set con un nuevo ‘breaking change’ dentro de la rama principal de Entity Framework. Este ‘breaking change’ se produce por el cambio en el modo de ejecución de las lecturas por parte de nuestras unidades de trabajo, pasando del modo habitual de lectura en streaming con un DbDataReader a un modo buffering, es decir, a la lectura temprana de los datos y al almacenamiento de los resultados en un buffer. A lo largo de esta entrada intentaremos ver  desde que supone para nosotros este cambio como desarrolladores, hasta las justificaciones para realizarlo, pasando como no, por algunas piezas de código que nos permitan entender mejor como lo han implementado.

El cambio

Cuando trabajamos con EF generalmente, solemos tener cosas como la siguiente:

 

Y todos tenemos claro que ese enumerador es una consulta diferida, técnicamente por un shaper, la diferencia de este trabajo en EF 6 con respecto a las anteriores estriba en como se hace la materialización de este enumerable. En la versión actual de EF tendremos un data reader que será usado en cada llamada al MoveNext del iterador. Aunque todos sabemos que internamente DataReader funciona con bloques para conjuntos de datos grandes estos se van incorporando a medida que se necesitan, es decir, hay un proceso de streaming de los datos del servidor a cliente. Para ver esto solamente tendríamos que realizar una lectura de un cojunto grande y ver como el BatchStarting y el BatchCompleted sucenden al comienzo de la lectura y cuando hemos iterado por todos los resultado, es decir, para la siguiente consulta, podríamos ver los siguientes valores en nuestro profiler

 

Comienzo de la lectura

Untitled1

 

final del bucle

Untitled2

 

Lógico según lo se sabemos sobre el comportamiento de nuestros data readers ¿ verdad ?. Bien, pues ahora intentamos hacer esto mismo con EF 6 y veremos lo siguiente nada más ejecutar el paso por el primero de los elementos…

 

image

 

La razón es la que comentamos más arriba, en la nueva versión de EF que tendremos el mecanismo de consultas reemplazará el DbDataReader por un BufferedDataReader (System.Data.Entity.Core.Objects.Internal.BufferedDataReader) el cual en realidad no es más que un envoltorio de un DataReader que hace todo la lectura de sus elementos en su inicialización y los almacena en una estructura interna BufferedDataRecord, de ahí el nombre de Buffered, que será usada por el materializador ( shaper ) para crear las distintas entidades.

Aunque esto pueda parecer un mero cambio interno a EF, en realidad no es tan sencillo y supone un breaking change porque tiene un impacto sobre las aplicaciones actuales. De entrada es lógico ver que hay una mayor presión de memoria, aunque sea memoria de generación 0, puesto que estamos duplicando los datos en el proceso de materialización. El segundo motivo es que por ejemplo si se rompe el bucle anterior por cualquier condición en la versión anterior no tendríamos porque haber traído todos los bloques del reader mientras que en EF 6 si habríamos traído todos los bloques del reader. Otro impacto posible lo podemos observar en la llamada a nuestro GetEnumerator, puesto que ahora necesitará más tiempo para completar el trabajo.

 

Los motivos

 

Las razones para movernos de un modo Streamed a Buffered se deben principalmente y tal como el equipo ha explicado en el siguiente design meeting a las siguientes razones:

 

  • Sql Azure connection resilience: Como todos sabéis, el trabajo con Sql Azure tiene una problemática especial que nos obliga a necesitar en ciertos escenarios de reintentos en nuestro trabajo con EF, Cesar de la Torre escribió hace ya mucho tiempo sobre esto. Dentro de la iniciativa del equipo por hacer más robusto el uso de EF con Sql Azure este cambio es necesario ya que no retarda el uso de una conexión más allá de lo necesario.
  • El tiempo de uso de una conexión se reduce potencialmente
  • Ya no es necesario MARS  ( obligatorio con lazy loading en la versión actual )

Y si quiero mi antiguo comportamiento

Bien sea por compatibilidad o porque el escenario así lo requiera, en EF tendremos un mecanismo para seguir usando streaming en nuestras consultas, para ello, solamente tendremos que utilizar el método AsStreaming, gracias al cual EF volverá a usar un DbDataReader en lugar del ya mencionado BufferedDataReader. A continuación podemos ver un ejemplo de ello:

 

 

Bueno, hasta aquí hemos llegado, espero que todos tengamos en cuenta este cambio si decidimos migrar aplicaciones de EF 5 a EF 6 puesto que tal y como hemos dicho puede tener impacto y producirse situaciones no deseadas sino nos fijamos…

 

Saludos

Unai

EF 6: Estableciendo las configuraciones de forma automática

La verdad es que no hace muchos días que publiqué un post con este trabajo para todos aquellos, que como yo, consideraran interesante el hecho de disponer de una forma automática de agregar las configuración de nuestro modelo, con el fin de facilitar el trabajo y prevenir ese código repetitivo que solemos ver muy a menudo. Para que no tengamos que recurrir a lo mismo en EF 6, me decidí por hacer un pequeño pull request que pudiera contener esta funcionalidad, ahora, que como ya sabréis todos, Entity Framework es Open Source  y, por lo tanto, abierto a que la comunidad pueda contribuir. Los que hayáis pasado alguna vez por el portal del proyecto en Codeplex sabréis que hay multitud de información sobre las diferentes vías de contribución, que no tienen porque ser solamente código, y como hacer estas contribuciones de una forma ordenada y consensuada. La palabra consensuada es importante aquí puesto que por mucho que a nosotros nos parezca importante y válida una contribución esta no tiene por que tener la misma validez para el equipo, por ejemplo, porque la pieza o piezas en las que vayamos a tocar se vayan a rediseñar o, a otra multitud de factores que se nos puedan escapar. El principio Don’t Push Your Pull Request es la norma a seguir dentro del equipo de desarrollo y por lo tanto el primer trabajo que tenemos que hacer es discutir con ellos lo que queremos hacer y por supuesto como lo vamos a hacer, en este caso, la sugerencia de pull request y su discusión la podéis ver aquí.

Bueno, después de ver el feedback y de las guías sobre como les gustaría la aportación, el resultado que ha quedado es este Change Set con la característica comentada anteriormente y, que pasaremos a desgranar ahora mismo.

 

Carga automática de configuraciones

 

Bien, esto es lo más básico y a la vez lo más natural de esta característica, puesto que como ya comentamos su primer trabajo es hacer la carga de configuraciones de forma automática, eliminando las típicas líneas repetitivas de carga de configuraciones, que en un proyecto real serán muchas y por lo tanto con un impacto en legibilidad y mantenibilidad. Es decir, lo que nos permitiría es cambiar cosas como esta:

 

Por lo siguiente:

Visibilidad de tipos y constructores

 

Para realizar esta carga automática de configuraciones no estamos restringidos a ninguna visibilidad de nuestras clases, ni de sus constructores. De hecho, esto es lo ideal, puesto que estas clases solamente tienen fines de configuración y no están pensadas para ser usadas más que por la infraestructura, parece lógico que pudiéramos escribirlas como por ejemplo en la siguiente de forma no pública y con un constructor privado.

 

 

Configuraciones en jerarquía

 

La última de las características de este trabajo, es el soporte a jerarquías de configuraciones, es decir, que nuestros tipos de configuración no implementen de forma directa EntityTypeConfiguration<>. Un ejemplo de este tipo de escenarios podría ser algo como lo siguiente:

 

Si observa, lo que hemos hecho, con meros fines de ejemplo, es hacer una pequeña base para nuestras configuraciones que nos agrega un prefijo tbl a los nombres de nuestras tablas. Esta configuración base la podríamos utilizar en todas aquellas entidades que nos interesaran que ya tuvieran OOB esta característica.

 

Pues bien, como hemos dicho, aún en estas situaciones nuestro AddFromAssembly seguirá funcionando perfectamente y cargando nuestras configuraciones de la forma correcta, excluyendo tipos abstractos y definiciones de tipos genéricos…

 

Bueno, espero que os resulte interesante esta novedad que tendremos en EF 6 y espero, que esta contribución os sea de la mayor utilidad posible, con ese fin está hecha…

 

saludos

Unai