LINQ to SQL (Parte 9 – Uso de expresiones LINQ personalizadas con el control )

En las últimas semanas he escrito una serie de post sobre LINQ to SQL. LINQ to SQL es un ORM que viene con .NET 3.5, y nos permite modelar bases de datos relacionales en clases. Podemos usar expresiones LINQ para consultar la base de datos y también para actualizar, insertar y borrar datos.

Aquí tenéis los enlaces a los diferentes post de la serie:

  • Parte 1: Introducción a LINQ to SQL
  • Parte 2: Definiendo el modelo de datos.
  • Parte 3: Consultando la base de datos
  • Parte 4: Actualizando la base de datos.
  • Parte 5: Enlazar controles de interfaz de usuario con el ASP:LinqDatSource
  • Parte 6: Obtener datos con procedimientos almacenados.
  • Parte 7: Actualizando la base de datos con procedimientos almacenados.
  • Parte 8: Ejecutar consultas SQL personalizadas.
  • En la quinta parte vimos el control <asp:LinqDataSource> de .NET 3.5 y hablamos sbre cómo podemos enlazar controles de ASP.NET a LINQ to SQL. También vimos cómo usarlo con el control <asp:ListView> (El control asp:ListView (Parte 1 - Creación de una página de listado de productos con una CSS limpia))

    En ambos artículos las consultas que hacíamos eran relativamente sencillas (la clausula where se ejecutaba sobre una tabla simple). En el post de hoy veremos cómo usar toda la potecia de las consultas de LINQ con el control LinqDataSource, y veremos cómo usar cualquier expresion LINQ to SQL con él.

    Pequeña recapitulación: <asp:LinqDataSource> con una sentencia Where.

    En estos dos  post vimos cómo usar el filtro del control LinqDatasource para declarar un filtro en un modelo LINQ to SQL.

    Por ejemplo, supongamos que hemos creado un modelo LINQ to SQL de la base de datos Northwind (que ya vimos en la segunda parte de esta serie), podríamos declarar un control <asp:LinqDataSource> en la página con un filtro <where> que devuelve aquellos productos de una categoría dada. (especificada a partir del valor "categoryid").

    Luego, podemos enlazar un <asp:gridView> a este datasource y habilitar la paginación, edición y ordenación.:

    Cuando ejecutamos la página anterior tendremos un GridView que soportará automáticamente la ordenación, paginación y edición sobre el modelo de Produt:

    Usando los parámetros del <where> de esta forma funciona muy bien en escenarios típicos. Pero ¿qué pasa si que el filtrado de Product sea más complejo? Por ejemplo, ¿Si sólo queremos mostrar los productos suministrados por un conjunto dinámico de paises?

    Uso del evento Selecting del <asp:LinqDataSource>

    En consultas personalizadas podemos implementar un manejador de eventos para el evento "Selecting" en el control <asp:LinqDataSource>. Con este manejador podemos escribir el código que queramos para obtener los datos. Esto lo podemos hacer con una expresión LINQ to SQL, o llamar a un procedimiento almacenado o usar una expresión SQL personalizada. Una vez que obtenemos la secuencia de datos, todo lo que tenemos que hacer es asignar la propiedad "Result" al objeto LinqDataSourceSelectEventArgs. El <asp:LinqDataSource> usará esta secuencia como los datos con los que trabajará.

    Por ejemplo, aquí tenéis una consulta LINQ to SQL que obtiene aquellos productos de los proveedores de un conjunto de países:

    VB:

    C#:

    Nota: No tenemos que escribir la consulta en el código del manejador. Una solución más limpia sería encapsularla en un método de ayuda al que podríamos llamar desde el propio manejador. Esto lo vimos en la parte 8 de esta serie (usando el método GetProductsByCategory).

    Ahora, cuando ejecutemos la página usando este manejador, sólo obtendremos los productos de los proveedores de ciertos países:

    Una de las cosas más interesantes es que la paginación y la ordenación siguen funcionando en nuestro GridView - aunque estemos usando el evento Selecting. Esta lógica de paginación y ordenación ocurre en la base de datos - es decir, sólo devolvemos los 10 productos de la base de datos que necesitamos para el índice actual del GridView (supereficiente).

    Os estaréis preguntando - ¿cómo es posible que tengamos una paginación y ordenación eficiente incluso cuando lo hacemos con un evento personalizado?. La razón es que LINQ usa el modelo de ejecución en diferido - es decir, la consulta no se ejecuta hasta que intentamos iterar sobre los resultados. Uno de los beneficios de este modelo es que nos permite componer consultas con otras consultas, y añadirles "comportamiento". Podéis leer más sobre esto en la tercera parte de la serie LINQ to SQL.

    En nuestro evento "Selecting" estamos declarando una consulta LINQ personalizada que queremos ejecutar y luego se la asignamos a la propiedad "e.Result". Pero aún no la hemos ejecutado (ya que no hemos iterado sobre los resultados o llamado a los métodos ToArray() o ToList()). El LINQDataSource es capaz de añadir automáticamente los operadores Skip() y Take() al aconsulta, así como aplicarle una expresión "orderby" -- siendo todos estos valores calculados automáticamente a partir del índice de página y las preferencias de ordenación del GridView. Sólo entonces el LINQDataSource ejecuta la expresión LINQ y obtiene los datos. LINQ to SQL se encarga de que la lógica de ordenación y paginado se haga en la base de datos - y que sólo se devuelvan 10 filas de productos.

    Fijáos cómo podemos seguir usando el GridView para editar y borrar datos, incluso cuando usamos el evento "Selecting" del LINQDataSource:

    El soporte de edicion/borrado funcionará mientras que el evento Selecting asigne la secuencia de resultados a la propiedad Result y sean objetos entidad (por ejemplo: una secuencia de Product, Supplier, Category, Order, etc). El LinqDataSource administrará los casos en el que los controles hagan actualizaciones sobre ellos.

    Para leer mas sobre cómo funcionan las actualizaciones con LINQ to SQL, leed la parte cuatro de esta serie,  y luego la parte quinta para ver las Updates en accción.

    Realizano proyecciones de consultas personalizadas con el evento Selecting.

    Una de las características más poderosas de LINQ es la habilidad de "formar" o "proyectar" datos. Podemos hacer esto en una expresión LINQ to SQL para indicar que queremos obtener sólo un subconjunto de valores de una entidad, y/o calcular nuevos valores dinámicamente al vuelo con expresiones personalizadas que definamos. Para leer más sobre esto leed la tercera parte de la serie.

    Por ejemplo, podemos modificar el evento "Selecting" para calcular un GridView para que muestre un subconjunto de información de Product. En el grid queremo mostrar el ProductID, ProductName, Product UnitPrice, el número de pedidos de ese producto, y el total de pedidos de ese producto. Podemos calcular estos dos últimos campos con la siguiente expresión LINQ:

    VB:

    C#:

    Nota: El método Sum para calcular el Revenue es un ejemplo de un método de extensión. La función es una expresión lambda. El tipo de resultados creados de la consulta LINQ es un tipo anónimo - ya que el tipo es inferido de la consulta. Métodos de extensión, expresiones Lambda, y los tipos anónimos son nuevas características de VB y C# en VS 2008.

    El resultado de esta expresión LINQ cuando lo enlazamos al GridView es el siguiente:

    Fijaos que la paginación y la ordenación sigue funcionando en el GridView - aunque estemos usando una proyección de LINQ para los datos.

    Una característica que no funcionará con las proyecciones es el soporte para la edición. Esto es debido a que estamos haciendo una proyección personalizada en el método Selecting, y el LINQDataSource no tiene forma de saber cómo actualizar la entidad. Si queremos añadir soporte para la edición en este caso, tendremos que crear un control ObjectDataSource (al que le pondremos un método Update personalizado para contorlarlos), o hacer que el usuario navegue a una nueva página para hacer la actualización - y mostrar un DetailsView o FormView enlazado a la entidad Producto para la edición (y no intentar hacerlo en el grid).

    Resumen

    Podemos realizar consultas personalizadas sobre el modelo LINQ to SQL usando el soporte integrado de filtrado del LINQDataSource.

    Para habilitar opiciones de filtrado más avanzadas, usaremos el método Selecting del LINQDataSource. Esto no permitirá crear la lógica que queramos para obtener y filtrar datos LINQ to SQL. Podemos llamar a métodos para obtener los datos, usar Expresiones LINQ, llamar a procedimientos almacenados  o invocar una expresión SQL personalizada para hacer esto.

    Espero que sirva.

    Scott.

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

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

    Artículo original:

    http://weblogs.asp.net/scottgu/archive/2007/09/07/linq-to-sql-part-9-using-a-custom-linq-expression-with-the-lt-asp-linqdatasource-gt-control.aspx

    13 pensamientos en “LINQ to SQL (Parte 9 – Uso de expresiones LINQ personalizadas con el control )

    1. Pingback: Dynamic LINQ(Parte 1: Usando la librería de LINQ Dynamic) « Thinking in .NET

    2. Francisco

      Estoy finalizando el tutorial y me parece magnifico. Tengo mucha suerte de haberme topado con este sitio felicitaciones y sigan adelante y sobre todo muchas gracias por compartir tan importante información.

      Responder
    3. Matías

      Excelente “trabajo”!!! jeje…Me ha servido muchísimo….espero ese post sobre la concurrencia!!

      Muchas Gracias!!!!!

      Responder
    4. KIRIO

      Excelente material, me pidieron hacer una investigación al respecto y gracias a estas publicaciones tan completas podré completar todo lo que necesito. Muchas gracias y éxitos.

      Responder
    5. Williams

      Excelente tutorial, soy nuevo en esto y me hacia falta algo de documentación sobre el manejo de datos con linq esto me ha ayudaddo vastante.tengo una clase de asp con c# y esto de verdad me ha ayudado mucho.mil gracias y que Dios te bendiga..

      Responder
    6. Adriana Cajina Marin

      Este tutorial ha estado magnifico, pero aun hay cosas que no puedo comprender.
      Como por ejemplo como insertar bajo una transacción. Es decir quiero insertar una factura y sus detalles pero para insertar los detalles necesito insertar la factura obtener el ID de la factura o la numeración generada y luego insertar los detalles con el ID de la factura. Todo en una transacción y por lo que he entendido SubmitChanges es como realizar un Commit hasta donde entiendo. Puedo realizar un Commit para insertar la factura solo para obtener el id de la factura y luego insertar los detalles de la misma? Y que pasa si falla la inserción de los detalles bajo el id de la factura? Como puedo hacer rollback?

      Responder
      1. Juanma Autor

        Hola Adriana, en el cuarto post de la serie tienes un ejemplo completo sobre el escenario que te interesa.
        Espero te sirva.

        Responder
    7. chelo

      Buenas! excelente serie de post, aprendí mucho con esto. En mi primer proyecto me tope con un problema que leí por ahí es un error ya reportado a MSN. Quisiera saber que me podés decir al respecto y si hay alguna solución. Tengo una tabla en la base de datos en donde uno de sus columnas acepta valores nulos, entonces, al momento de insertar un nuevo registro en ella, por ejemplo por medio de Executecommand, y quiero informarle justamente un valor nulo a ese campo, internamente Linq transforma a ese valor a DBNull, y ésto no es reconocido como un valor nulo por el compilador, lo que provoca una exception en la query.
      Felicidades por los post.

      Responder

    Deja un comentario