Artículo :: Programación :: ADO.NET
¿Trabaja el objeto DataReader realmente en un ambiente conectado?
Leía el otro día el comentario de mi compi Pablo Álvarez Doval en su blog acerca de si los DataReader utilizaban o no cursores, cuando me acordé de la explicación que he dado en varias ocasiones, y en concreto en las cabañas que hice en el lanzamiento de Visual Studio 2005, acerca de si los DataReader están realmente conectados o no.
Por esa razón, voy a aprovechar en esta ocasión, para hablar de ese tema que tan bien viene después de los comentarios de Pablo (como siempre, se aceptan críticas, opiniones, demostración y demás berenjenas de la huerta).
Todos sabemos que los DataReader son usados en .NET para trabajar con datos estando conectados a la fuente de datos, ahora bien, la pregunta es sencilla. ¿Cómo trabaja exactamente los objetos DataReader?, ¿trabajan realmente en un ambiente conectado?.
Para tratar de responder a esta pregunta, podemos basarnos perfectamente en el comentario de Pablo, porque ahí mismo, nos da en buena parte la clave de como funciona un DataReader al trabajar en este caso, con SQL Server.
Comenzaré comentando la prueba que he realizado, y finalmente las conclusiones.
Para este ejemplo, he creado un simple formulario Windows al que he insertado un control TextBox, un control Button y un control Label.
El código que he escrito es el siguiente:
|
Imports System.Data.SqlClient
Public Class Form1
Private conexion As SqlConnection Private comando As SqlCommand Private filas As Integer = 0 Private lector As SqlDataReader
Private Function ObtenerConexion() As String Return "Data Source=.\SQLEXPRESS;Initial Catalog=AdventureWorks;Integrated Security=True" End Function
Private Function ObtenerSQL() As String Return "SELECT * FROM Person.Contact" End Function
Private Sub Form1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Leave conexion.Close()
comando.Dispose()
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load conexion = New SqlConnection(ObtenerConexion) comando = New SqlCommand(ObtenerSQL, conexion) conexion.Open() lector = comando.ExecuteReader() End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Try lector.Read() Me.TextBox1.Text = lector("FirstName") & " " & lector("LastName") filas += 1 Me.Label1.Text = filas & " fila (s) de datos" Catch ex As Exception ' NADA Me.Label1.Text = ex.Message End Try End Sub
End Class ' Form1
|
Como podemos apreciar en el código, hemos utilizado como base de datos, la base de datos de ejemplo de Microsoft AdventureWorks en Microsoft SQL Server 2005 Express Edition. También me he provisto de la ventana de Servicios, la cuál usaré para detener el servicio de SQL Server 2005 Express Edition para estudiar los comportamientos de la recogida de datos.
Seguimos... :-)
Si ejecutamos este pequeño ejemplo, accederemos a los datos de la base de datos AdventureWorks e iremos navegando por ellos uno detrás del otro... hacia adelante... forward-only, read-only como bien indicaba Pablo en su comentario.
Sin embargo, este funcionamiento se basa en un buffer. La pregunta en este punto, es si el buffer está en el lado de SQL Server o en el lado de los objetos de ADO.NET... buena pregunta ¿verdad?.
El caso es que en este punto, la primera prueba (sencilla) es ejecutar nuestro ejemplo y ver el comportamiento de nuestro programa a la hora de pulsar el botón una vez tras otra. Parece que funciona... ¡bien!. Sigamos un poco más...
A continuación, ejecutaremos nuestra demo, haremos clic en el botón de nuestra demo, observaremos que lee el primer registro y acudiendo al Servicio de SQL Server (SQL EXPRESS), pulsaremos sobre el botón detener servicio. Pulsaremos nuevamente el botón y veremos que lee el siguiente registro. Pulsaremos el botón una y otra vez hasta que veamos que no lee más registro, mostrando una información de error en la etiqueta o control Label.
Si prestamos atención al error, el mensaje es un error de transporte de canalización de datos.
En mi caso el error se produce al leer el tercer registro, detener el servicio y querer leer el cuarto registro.
Así que he pensado que si hay un buffer por medio que recupera en este ejemplo tres registros, es lógico pensar que leerá los datos hasta el tercer registro incluido, y cuando quiera leer el cuarto, meterá el cuarto en mi aplicación y leerá el siguiente bloque para meter en el buffer que serán aproximadamente otros tres registros, así que si leo el cuarto registro y detengo el servicio, la lectura de registro finalizará en el séptimo.
Tenemos que tener en cuenta, que el buffer rellena datos y que estas pruebas pueden oscilar dependiendo de diferentes factores muy generales, así que tomemos estos datos como ajustables y dependientes de la máquina, por lo que son datos aproximativos.
En resumidas cuentas, la lectura se realiza como se indica en las siguientes imágenes:
Observando esta secuencia, podemos ver que en primer lugar, se rellena un buffer con datos, los registros 1, 2 y 3.
La aplicación va leyendo estos datos, así hasta llegar al tercer registro. Al leer el cuarto, el cuarto registro que no está en el buffer, el DataReader se va a por ese registro a la fuente de datos, lo recupera y rellena el buffer con otros tantos registros hasta llenar el buffer.
Este ciclo se repite n veces hasta completar la lectura de los datos solicitados.
Es decir, el proceso de lectura con DataReader no es un proceso de lectura conectado puro, y por lo tanto, no realiza una lectura contra la base de datos cada vez que lee el siguiente registro, sino que se nutre de un buffer para recuperar registros, algo completamente lógico, ya que sino, el torpedeo y lectura constante a la fuente de datos, mermaría el rendimiento de la misma, por muy pequeño que sea dicho rendimiento, algo que no vamos a entrar a discutir ahora y que dependería de múltiples factores (red, hardware, software, dimensionamiento, etc etc etc).
El quiz de la primera pregunta queda resuelto en tanto en cuanto, hemos visto que aunque un DataReader trabaja de forma conectada, no lo hace de forma continua y sólo bajo demanda, bajo la demanda del buffer de relleno, pero aún en mi opinión, queda otra duda pendiente... ¿dónde está ese buffer?. ¿Está en SQL Server o en ADO.NET?.
En el gráfico no lo he pintado, porque el buffer... ¡no sabemos realmente dónde está!. Así que eso mismo, es lo que demostraremos a continuación.
Para resolver esta segunda cuestión, nada mejor que trabajar en red con un SQL Server distribuido... el mismo SQL Server 2005 Express por ejemplo.
Pondremos esa fuente de datos en un servidor, y ejecutaremos nuestra aplicación de demo en otro PC. Así, al PC que sirve los datos (SQL Server), le quitaremos la conexión de red cuando la aplicación comience a leer los datos del buffer. De este modo, demostraremos si el buffer está en el servidor de SQL Server o por el contrario en la parte local, es decir, en ADO.NET, al cuál accedería DataReader.
Ejecutaremos nuevamente nuestra aplicación cambiando la cadena de conexión para apuntar a otro servidor, leeremos el primer registro, y quitaremos del PC dónde tenemos SQL Server el cable de red. ¿Que ocurre?.
Lo que ocurre es que el buffer que teníamos siguepermitiéndonos leer los datos. Lo mismo ocurre con el dibujo anterior, si llego al tercer registro y quito el cable de red, al intentar leer el cuarto registro de SQL Server dará un error. Si por el contrario, leemos los datos hasta llegar al cuarto registro y en ese momento quito el cable de red, la aplicación leerá el quinto, sexto y séptimo registro, pero al intentar leer el octavo, acudirá al servidor y devolverá un error.
La conclusión de todo esto en mi opinión, es la siguiente:
1) Los objetos DataReader no son puramente conectados como tal en cuanto a la petición bajo demanda. Si son conectados, pero trabajan con un buffer en local (no en el servidor) que recupera del servidor para trabajar con datos más rápidamente.
2) El buffer con el que trabaja DataRader, no está en el servidor de bases de datos, sino en el propio sitio local.
Espero que estas afirmaciones no creen ampollas a nadie y sí que invito a que los que quieran, hagan sus pruebas y comenten sus resultados, pues aún y cuando estoy terminado este pequeño artículo, tengo un par de dudas sobre algunos comportamientos que seguiré investigando.
Cross Posted from Jorge Serrano - MVP Visual Developer - VB