LINQ to SQL (3ª Parte – Consultando la base de datos)

El mes pasado empezé una serie de post sobre LINQ to SQL. LINQ to SQL es un framework O/RM (Object relational mapping) que viene como parte del .NET Framework 3.5, que nos permite modelar de forma sencilla bases de datos relacionales con clases de .NET. Podemos usar, por tanto, expresiones LINQ tanto para consultar a la base de datos como para actualizar/inertar/borrar datos.

Aquí tenéis los enlaces a los primero dos post de esta serie:

En el post de hoy vamos a ver en más detalle cómo usar el modelo de datos que creamos en la segunda parte, y veremos cómo usarlo para consultar datos en un proyecto ASP.NET.

Modelo de la base de datos Northwind con LINQ to SQL

En el segundo post de la serie vimos cómo crear un modelo de clases LINQ to SQL usando el diseñador de LINQ to SQL que viene con VS 2008. Aquí tenéis el modelo que creamos a partir de la base de datos de ejemplo Northwind:

Obteniendo productos.

Una vez que tenemos definido nuestras clases del modelo de datos, podemos consultar y obtener fácilmente datos de nuestra base de datos. LINQ to SQL nos permite esto usando la sintáxis de consultas de LINQ sobre la clase NorthwindDataContext que creamos con el diseñador LINQ to SQL.

Por ejemplo, para obtener e iterar sobre una secuencia de objetos Product podemos escribir el siguiente código:

En esta consulta hemos usado la sentencia "where" en nuestra consulta LINQ para devolver aquellos productos de una categoría. Estamos usando el campo/propiedad CategoryID del producto para hacer el filtro.

Una de las cosas que nos aporta LINQ to SQL es que nos da una total flexibilidad en cómo consultar nuestros datos, y podemos aprovecharnos de las asociaciones que hicimos cuando modelamos las clases de LINQ to SQL para hacer consultas más naturales y ricas sobre la base de datos. Por ejemplo, podemos modificar el filtro de la consulta por el CategoryName en lugar de por el CategoryID con la siguiente consulta LINQ:

Fijáos en cómo estamos usando la propiedad "Category" de cada objeto Product para filtrarlos por CategoryName. Esta propiedad fue creada automáticamente por LINQ to SQL ya que modelamos las clases Category y Product con una relación "varios a uno" en la base de datos.

Por poner otro ejemplo del uso de las relaciones de nuestro modelo, podríamos escribir la siguiente consulta LINQ para obtener aquellos productos que tengan más de cinco órdenes para ellos:

Fijáos cómo usamos la colección "OrderDetails" que LINQ to SQL creó en cada clase Product (debido a la relación 1 a varios que modelamos en el diseñador LINQ to SQL).

Visualizando consultas LINQ to SQL en el debugger

Los ORM como LINQ to SQL administran automáticamente la creación y la ejecución del código SQL cuando realizamos consultas o actualizaciones sobre su modelo de objetos.

Una de los mayores preocupaciones que tienen los desarrolladores sobre los ORMs es "¿pero qué código SQL se está ejecutando?" Una de las cosas que hace LINQ to SQL es poder ver exáctamente qué código SQL se está ejecutando cuando ejecutamos nuestra aplicación con el debugger.

Con la beta 2 de VS 2008 podemos usar el nuevo plug-in de visualización LINQ to SQL para ver (y testear) cualquier consulta LINQ to SQL. Simplemente añadimos un breakpoint y pasamos el ratón por encima y hacemos clic en la lupa para visualizar esa consulta:

ESto nos mostrará un cuadro de diálogo que nos dirá exactamente la SQL que LINQ to SQL usará cuando se ejecute la consulta para obtener los objetos Product:

Si pulsamos el botón "Execute" de este diálogo nos permitirá evaluar el SQL dentro del debugger y nos mostrará los resultados de la base de datos:

Obviamente esto hace realmente fácil ver qué lógica de consultas SQL está realizando LINQ to SQL. Fijáos que podemos sobreescribir la SQL que LINQ to SQL ejecutará si queremos cambiarlo - sin embargo, en el 98% de los casos creo que os dareis cuenta de que el código SQL que LINQ to SQL ejecuta es realmente bueno.

Enlazando consultas LINQ to SQL a controles ASP.NET

Los resultados de las consultas LINQ implementa la interfaz IEnumerable - la cual es una interfaz que los controles de servidor de ASP.NET soportan para enlazar datos. Lo que implica que podemos enlazar los resultados de cualquier consulta LINQ, LINQ to SQL, o LINQ to XML a cualquier control ASP.NET.

Por ejemplo, podemos declarar un control <asp:gridview> en una página .aspx de la siguiente forma:

Luego, podemos enlazar los resultados de la consulta LINQ to SQL que escribimos antes:

Esto generará una página como la siguiente:

Restringiendo los resultados de la consulta.

Hasta ahora, cuando evaluamos una consulta de productos, estamos obteniendo por defecto todas las columnas de datos necesarias para cubrir la entidad de Product.

Por ejemplo, esta consulta para obtener productos:

El resultado de esta consulta es:

Normalmente sólo queremos un subconjunto de los datos de cada producto. Podemos usar la nueva característica que LINQ y los compiladores de C# y VB tienen para indicar que sólo queremos un subconjunto de los datos, modificando la consulta LINQ to SQL de la siguiente forma:

Con esto obtendremos un subconjunto de los datos que se obtienen de la base de datos (como vemos con el visor del debugger):

Lo realmente útil de LINQ to SQL es que podemos aprovecharnos de las asociaciones entre clases de nuestro modelo de datos cuando restringimos los datos. Esto nos permite expresar consultas útiles y muy eficientes. Por ejemplo, la siguiente consulta obtiene los ID y los nombres de la entidad Product, el número total de pedidos que hemos hecho de productos, y los suma al total de pedidos de Productos:

La expresión a la derecha de la propiedad "Revenue" es un ejemplo del uso del método de extensión "Sum" de LINQ. Toma una expresión Lambda que devuelve el valor de cada pedido de producto como argumento.

LINQ to SQL es listo y es capaz de transformar la expresión LINQ anterior al siguiente SQL cuando es evaluado (con el visor del debugger):

La sentencia SQL anterior hace que los valores NumOrders y Revenue se calculen dentro del servidor SQL, y devuelve los siguientes valores de la base de datos (realmente rápido):

Podemos enlazar el resultado anterior a nuestro gridview:

BTW - en caso de que os lo preguntéis, tenemos intellisense en VS 2008 cuando escribimos estos tipos de restricciones en las consultas LINQ:

En este ejemplo estamos declarando un tipo anónimo que usa la inicialización de objetos para amoldar y definir la estructura del resultado. Y seguimos teniendo intellisense en VS 2008, chequeo de compilación y soporte para refactoring con estos tipos anonimos:

Paginando los resultados de la consulta.

Una de las necesidades más comunes en entornos web es la posibilidad de hacer eficientemente la paginanción en las interfaces de usuario. LINQ tiene dos métodos de extensión que permite hacer esto de forma fácil y eficiente - los métodos Skip() y Take().

Podemos usar los métodos Skip() y Take() para indicar que sólo queremos devolver 10 objetos producto - desde la fila que le pasemos como argumento:

Fijáos que no añadimos ni Skipt() ni Take() en la primera consulta - sino que lo hacemos después de la consulta (cuando lo enlazamos a la fuente de datos del GridView). Muchos me preguntan "¿pero esto no significa que primero obtiene todos los datos de la base de datos y luego hace la paginación (esto es malo)?" No. La cuestión es que LINQ usa un modelo de ejecución en diferido, es decir, la consulta no se ejecuta hasta que se itera sobre los resultados.

Uno de los beneficios de este modelo de ejecución en diferido es que nos permite crear consultas en varias líneas de código (lo que mejora la claridad). También nos permite crear las consultas después de otras - lo que nos permite composiciones más flexibles y reutilización.

Una vez que tenemos el método BindProduct(), podemos escribir el siguiente código en nuestra página para obtener el índice de inicio de la consulta y hacer que los productos sean paginados y mostrados en el gridview:

Esto nos dará una página de productos, filtrada para mostrar aquellos productos que tengan más de cinco pedidos, mostrando datos calculados dinámicamente, y que son paginables a partir de una cadena de consulta:

Nota: Cuando trabajamos contra SQL 2005, LINQ to SQL usará la función SQL ROW_NUMBER() para crear toda la lógica de paginación en la base de datos. Esto nos asegura que sólo devolverá las 10 filas de datos que queremos  mostrar en la página:

Esto hace realmente fácil y eficiente navegar por grandes cantidades de datos.

Resumen

Hemos visto por encima alguna de las cosas que LINQ to SQL nos ofrece. Para aprender más sobre expresiones LINQ y las nuevas características de consultas que traen los compiladores de C# y VB con VS 2008, leed estos post:

En el próximo post de esta serie sobre LINQ to SQL veremos cómo podemos añadir lógica de validación a nuestro modelo de clases de datos, y mostraremos cómo podemos usarlo para encapsular la lógica de negocio que se ejecutará con cada actualización, inserción o borrado de nuestros datos. Veremos casos más avanzados, cómo usar el nuevo control <asp:LINQDataSource> para añadir enlaces de datos declarativos a controles ASP.NET, resolución de errores de concurrencia optimista, y más.

Espero que sirva.

Scott.

 Traducido por: Juan María Laó Ramos. Microsoft Student Partner.

toH tlhIngan Hol DajatlhlaH ‘e’ DaneH’a’?

20 pensamientos en “LINQ to SQL (3ª Parte – Consultando la base de datos)

  1. Pingback: Visor de Debugging de LINQ to SQL « Thinking in .NET

  2. Pingback: El control asp:ListView (Parte 1 - Creación de una página de listado de productos con una CSS limpia « Thinking in .NET

  3. Pingback: LINQ to SQL (Parte 6 - Obtener datos con procedimientos almacenados) « Thinking in .NET

  4. Pingback: ASP.NET MVC Framework (Primera parte) « Thinking in .NET

  5. Darko

    Muchas gracias, no es necesario el codigo “anexo”, pues para un programador principiante esta apenas entendible, ( yo lo soy, y entiendo ).

    Muchas gracias, logre emplear uno que otro ejercicio de manera exitosa,
    ( claro despues de leer varias veces y seguir paso a paso jaja )

    El secreto para entender algo, es dejar el afan y ponerte a ensayar.

    Muchas gracias.

  6. esteban

    Hola, podrias terminar de ingresar el codigo que no entro completo en la imagen de la paginación, dice asi:
    protected void Page_Load(object sender, EventArgs e)
    {
    int startrow = Convert.ToInt32(Request.QueryString[ “?????????”

    Muy buenos los post, soy principiante y pude realizar todos los ejercicios hasta este nivel sin problemas.

  7. esteban

    Me di cuenta que si copio y pego las imagenes en el paint o algun otro programa me sale el codigo completo. Me lo estaba tapando la linea gris de la derecha. Gracias igual. Muy bueno el efecto de la nieve!!!

  8. Omar

    Hola, tengo una duda, como puedo colocar valores devueltos en un grupo de textbox?, ya que solo aparecen ejemplos para llenar un gridView o cuando solo devuelve un dato.
    Saludos.

  9. Leonel

    Excelente tutorial compañero, muchas gracias por compartir tus conocimientos con personas como yo que están ansiosos en aprender programación.
    Tengo una consulta, y es que deseo saber como realizar un filtro en tiempo de ejecución a un datagrid llenado con una consulta en LINQ, lo que deseo es que en un textbox en el momento de ir ingresando letras desde el ordenador el valla filtrando los valores de acuerdo a orden en que estoy escribiendo.

    De antemano muchas gracias por la ayuda brindada.

  10. Pedro Sandoval

    Estoy desarrollando una aplicacion muy similar a orden de compra cuando hago cambios en las cantidades de los productos en el detalle se graban bien en la tabla pero cuando limpio la pantalla para llamar a otro y decido llamar al mismo que estaba trabajando me trae los datos bien pero las cantidades de los detalles que habia cambiado pero las anteriores ( Nota estoy trabajando con vs-20120 con sql-2008 ) y las consultas la hago con LINQ . paraece que hay un desface entre la BD y el modelo Linq ?

  11. Carlos

    Hola alguien sabe como puedo pasar los campos de un registro de una consulta a correspondientes textbox en mi interfaz es decir lo que quiero por ejemplo si realizo una consulta en mi tabla cuentasDeServiciosTab con los campos nombre,apellidos,email,telefono etc, que en un textbox pueda mostrar el nombre en otro el apellido y asi sucesivamente es que todos los manuales y paginas siempre llenan datagrids o gridview, y he buscado por dias y no logro encontrar algo que supongo q debe ser basico, por su ayuda mil gracias

    1. Juanma Autor

      Hola Carlos, si te he entendido bien deberías hacer:
      miTextBox.Text=”mi nombre”
      Y así con todos y cada uno de los campos que te interese.
      Espero que sirva

  12. isacko

    Muchas gracias x compartir y tomarte el tiempo para explicarlo detalladamente! Muy buen post!

  13. mariano

    fijate que se te desconfiguro todo el texto, pero mas alla de eso, genial aporte

Los comentarios están cerrados.