usando linq to sql
TRANSCRIPT
Usando LINQ to SQL (1ª Parte)
En los últimos meses he escrito una serie de post que cubrían algunas de las característcias que van a venir con Visual
Studio y .NET Framework “Orcas”. Aquí tenéis los enlaces:
Propiedades automáticas, inicializadores de objectos e inicializadores de colleciones.
Métodos de extensión.
Expresiones Lambda .
Sintaxis de consultas.
Tipos Anónimos
Las características anteriores hacen que la consulta de datos sea un concepto de primera clase. Conocemos a este
modelo de programación como “LINQ” – que viene de .NET Language Integrated Query.
Los desarrolladores pueden usar LINQ con cualquier fuente de datos. Pueden expresar consultas eficientemente
en los lenguajes de programación que eligan, opcionalmente transformar/incrustar los resultados de las consultas
en el formato que quieran, y entonces manipular fácilmente los resultados. Los lenguajes habilitados para LINQ
pueden aportar seguridad de tipos y chequeo en tiempo de compilación el las expresiones de consulta, y
desarrollar herramientas que aporten intelisense, debugging, y un gran soporte para refactoring cuando escriban
código de LINQ.
LINQ soporta un modelo de extensibilidad muy rico que facilita la creación de operadores eficientes para fuentes de
datos. La versión “Orcas” del .NET Framework biene con librerías que habilitan LINQ sobre objetos, XML y bases de
datos.
¿Qué es LINQ to SQL?
LINQ to SQL es una implementación de O/RM(object relational mapping, mapeador de objetos relacionales) que viene
con la versión “Orcas” del .NET Framework, y nos permite modelar bases de datos relacionales con clases de .NET.
Podemos consultar bases de datos con LINQ, así como actualizar/añadir/borrar datos de ellas.
Modelando bases de datos con LINQ to SQL:
Visual Studio “Orcas” viene con un diseñador de LINQ to SQL que nos aporta una forma fácil de modelar y visualizar
una base de datos como un modelo de objeto de LINQ to SQL. El próximo post cubrirá en más profundidad cómo usar
este diseñador (podéis ver éste video que hice en Enero para verme construir un modelo LINQ to SQL).
Usando ese diseñador LINQ to SQL puedo crear fácilmente una representación de la base de datos “Northwind”:
El diseño de arriba define cuatro clases: Product, Category, Order y OrderDetail. Las propiedades de cada clase
mapean las columnas de cada table en la base de datos. Cada instancia de esa clase representa una fila en las tablas.
Las flechas entre las cuatro clases de arriba representan las asociaciones/relaciones entre las diferentes entidades.
Son típicamente modeladas como relaciones primary-key/foreign-key en la base de datos. La dirección de las flechas
en el diseñador indican si la relación es uno-a-uno o uno-a-varios. Se añadiran propiedades fuertemente tipadas a las
entidades basándose en esto. Por ejemplo, la clase Category de arriba tiene una relación de uno-a-varios con la clase
Product. Esto implica que tendrá una propiedad “Categories” que es una colección de objetos Product con esa
categoría. La clase Product entonces tiene una propiedad “Category” que apunta a una instancia de la clase Category
representando la categoría a la que pertenece el producto.
El panel de la derecha del diseñador LINQ to SQL contiene una lista de procedimientos almacenados que interactúan
con nuestro modelo de base de datos. En el ejemplo de arriba hemos añadido un SPROC (Procedimiento almacenado)
“GetProductsByCategory”. Como entrada recibe un categoryID, y devuelve una secuencia de Product como resultado.
Veremos cómo llamar a este procedimiento almacenado en un ejemplo.
Entendiendo la clase DataContext
Cuando pulsáis el boton “save” del diseñador de LINQ to SQL, Visual Studio generará clases .NET para representar las
entidades y las relaciones de la base de datos que hemos modelado. Por cada archivo añadido a nuestra solución por
el diseñador LINQ to SQL también se generará una clase DataContext. Esta clase es a traves de la cual realizaremos
las consultas a las entidades de nuestra base de datos. Esta clase tendrá propiedades que representarán a cada tabla
que hemos modelado, así como métodos para cada procedimiento almacenado que añadamos.
Por ejemplo, aquí tenéis la clase NorthwindDataContext:
Ejemplos de LINQ to SQL
Una vez que hemos modelado nuestra base de datos con el diseñador de LINQ to SQL, podemos escribir código
fácilmente para trabajar con él. Aquí tenéis unos cuantos ejemplos que muestran tareas comunes con datos:
1) Consultando Products de la base de datos
El siguiente código usa una consulta LINQ para obtener una secuencia IEnumerable de objetos Product. Fijáos que este
código está consultando a traves de la relación Product/Category para obtener aquellos productos de la categoría
“Beverages”.
C#:
VB:
2) Actualizando un producto en la base de datos.
El código siguiente muestra cómo obtener un producto de la base de datos, actualizar su precio, y guardar los cambios
en la base de datos:
C#:
VB:
Nota: VB en “Orcas” Beta1 no soporta Lambdas aún. Pero en la Beta2 sí -de forma que el código anterior se podrá
escribir de una forma más concisa.
3) Añadir una nueva categoría y dos nuevos productos en la base de datos.
El siguiente código muestra cómo crear una nueva categoría, y entonces crear dos nuevos productos y asociarlos a la
nueva categoría. Los tres son después guardados en la base de datos.
Fijaos como no necesitamos administrar manualmente las relaciones primarykey/foreignkey. Sólo tenemos que añadir
los objetos Product en la colección “Products” de la categoría, y luego añadir el nuevo objeto Category en la colección
de “Categories” del DataContext, LINQ to SQL sabrá automáticamente crear las PF/FK necesarias:
C#:
4)Borar productos de la base de datos.
El código siguiente muestra cómo borrar todos los productos Toy de la base de datos:
C#:
VB:
5) Llamar a un procedimiento almacenado.
El código siguiente muestra cómo obtener entidades de la tabla Product sin usar una consulta LINQ, sino llamando al
procedimiento almacenado “GetProductsByCategory” que añadimos a nuestro modelo de datos. Fijáos que cuando
obtenemos los resultados de la tabla Product, podemos actualizar/borrarlos y llamar a db.SubmitChanges() para hacer
las modificaciones en la base de datos.
C#:
VB:
6) Obtener productos con paginado del lado del servidor
El código siguiente muestra cómo implementar un paginado eficiente en el lado servidor como parte de una consulta
LINQ. Usando los operadores Skip() y Take(), sólo devoleremos 10 filas de la base de datos – a partir de la fila 200.
C#:
VB:
Resúmen:
LINQ to SQL nos permite modelar la capa de datos de nuestras aplicaciones de una forma simple y limpia. Una vez que
hayamos definido nuestro modelo de datos, podemos realizar consultas, inserciones, actualizaciones y borrados sobre
ella de forma fácil y eficiente.
LINQ to SQL (2ª Parte – Definiendo nuestras clases del modelo de datos)
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Visual Studio a 12:06 am por Juanma
En la primera parte de la serie de post sobre LINQ to SQL hablé sobre “¿qué es LINQ to SQL?” y vimos por encima
algunos escenarios que permite.
En aquél post pusimos unos cuantos ejemplos de código donde demostrábamos cómo mejorar la parte de datos
usando LINQ to SQL:
Cómo consultar una base de datos.
Cómo actualizar filas en una base de datos
Cómo añadir y relacionar varias filas en una base de datos.
Cómo eliminar filas de la base de datos.
Cómo llamar a procedimientos almacenados.
Cómo obtener datos con paginación en el servidor.
Mejoramos todos estos escenarios usando un modelo de clases de LINQ to SQL como éste:
En este segundo post de la serie vamos a ver en más detalle cómo crear el modelo anterior con LINQ to SQL.
LINQ to SQL, el diseñador de LINQ to SQL, y todas las características que estamos viendo saldrán con la versión
de .NET 3.5 y en la release de Visual Studio “Orcas”.
Podéis seguir todos los pasos siguientes descargándo tanto Visual Studio “Orcas” Beta 1 o Visual Web Developer
Express “Orcas” Beta 1. Podéis instalar las dos y usarlas sin ningún problema con Visual Studio 2005.
Crear un nuevo modelo de datos LINQ to SQL
Podemos añadir un modelo de datos de LINQ to SQL a un projecto ASP.NET, Class Library o Windows, con la nueva
opción “Add New Item” seleccionando “LINQ to SQL”:
Seleccionando “LINQ to SQL” lanzará el diseñador de LINQ to SQL, y nos permitirá modelar las clases que representen
una base de datos relacional. También creará una clas fuertemente tipada “DataContext” que tendrá las propiedades
que representarán cualquier tabla que modelemos de la base de datos, así como métodos para cada procedimiento
almacenado que modelemos. Como describimos en la primera parte de esta serie de post, la clase DataContext es el
conducto principal que usaremos tanto para consultar la base de datos como para guardar los cambios que hagamos.
Aquí tenéis una captura de pantalla de un diseño LINQ to SQL ORM vacío, es lo que veréis despues de crear un nuevo
modelo de datos LINQ to SQL:
Clases Entidad (Entity)
LINQ to SQL nos permite modelar clases que mapeen una base de datos. Estas clases son típicamente conocidas como
“Clases Entidad” (en ingles “Entity Classes”) y a las instancias se las conoce como “Entidades” (en ingles “Entities”).
Las clases entidad mapean a tablas de una base de datos. Las propiedades de una clase entidad normalmente
mapean las columnas de la tabla. Cada instancia de una clase entidad representa a una fila de una tabla de la base de
datos.
Las clases entidad definidas por LINQ to SQL no tienen que derivar de una clase base específica, lo que significa que
pueden heredar de cualquier objeto que queramos. Todas las clases creadas por el diseñador de LINQ to SQL se
definen como “clases parciales” – con lo que podemos, opcionalmente, añadir propiedades adicionales, métodos y
eventos.
A diferencia de la característica de DataSet/TableAdapter que aporta VS 2005, cuando usamos el diseñador de LINQ to
SQL no tenemos que especificar qué consultas SQL se tiene que usar cuando creamos el modelo de datos y la capa de
acceso.
En lugar de eso, nos centramos en definir las clases entidad, cómo se mapean con la base de datos, y las relaciones
entre ellas. La implementación del ORM de LINQ to SQL se encargará de generar la lógica de ejecución SQL por
nosotros en tiempo de ejecución para que podamos interactuar y usar las entitades de datos. Podemos usar sintaxis
de consultas LINQ para indicar cómo consultar nuestro modelo de datos de forma fuertemente tipada.
Crear clases entidad de la base de datos.
Si ya tenemos un esquema de base de datos definido, podemos usarlo para crear clases entidad LINQ to SQL.
La forma más sencilla de conseguirlo es abrir la base de datos desde el “Server Explorer” de Visual Studio, seleccionar
las tablas y vistas (Views) que queramos modelar, y arrastrarlas al diseñador LINQ to SQL:
Cuando añadimos estas dos tablas (Categories y Products) y una vista (Invoices) de la base de datos “Northwind” al
diseñador de LINQ to SQL, tendremos las siguientes clases entidad creadas a partir del esquema de la base de datos:
Usando estas clases, podemos ejecutar todos los ejemplos de código (excepto el de procedimientos almacenados) que
vimos en la primera parte de esta serie sobre LINQ to SQL. No tenemos que añadir ningún código adicional o
configuración para habilitar los escenarios de consulta, inserción, actualización, borrado, y paginación en el servidor.
Nombrado y pluralización
Una de las cosas de las que os daréis cuenta usanto el diseñador de LINQ to SQL es que automáticamente “pluraliza”
los nombres de las tablas y columnas cuando crea las clases entidad basádas en el esquema de la base de datos. Por
ejemplo: la tabla “Products” del ejemplo se resuelve en una clase “Product”, y la tabla “Categories” se resuelve en la
clase “Category”. Este nombrado de clases hace que vuestro modelo sea más consistente con las convenciones de
nomenclatura de .NET, y encuentro bastante útil que el diseñador haga esto por mi (especialmente cuando añadimos
muchas tablas a nuestro modelo).
Si no os gusta el nombre de una clase o propiedad que el diseñador ha generado, siempre podréis cambiarlo por el
que queráis. Podéis hacerlo editanto el nombre de la entidad/propiedad en el mismo diseñador o cambiarlo en la rejilla
de propiedades:
Esta habilidad de nombrado de entidades/propiedades/asociaciones es muy útil en un gran número de casos. En
particular:
1) Cuando cambie el nombre de una tabla/columna de vuestra base de datos. Como vuestras entidades tendrán
nombres diferentes, podéis decidir actualizar las reglas de mapeado y no el código de vuestra aplicación o las
consultas para usar esas nuevas tablas/columnas.
2) Cuando en el esquema de la base de datos tengais nombres que no son “limpios”. Por ejemplo, en lugar de usar
“au_lname” y “au_fname” para los nombres de las propiedades en una clase entidad, podéis usar los nombres de
“LastName” y “FirstName” en vuestras clases entidad y programar con esos nombres, en vez de cambiarlo en la base
de datos.
Relaciones
Cuando arrastremos objetos del “server explorer” al diseñador “LINQ to SQL”, Visual Studio comprobará las relaciones
de clave primaria y ajenas de los objetos, y basándose en ellas creará relaciones por defecto entre las diferentes
clases entidad que genere. Por ejemplo, cuando añadimos las tablas Products y Categories de la base de datos
NorthWind al diseñador LINQ to SQL podemos ver que se ha deducido una relación de uno a n entre ellas (esto se
indica con la felcha del navegador):
Esta relación hará que la clase entidad Product tenga una propiedad llamada “Category” que los desarrolladores
usarán para acceder a la entidad Category para un Product dado. También hará que la clase Category tenga una
colección de “Products” que permitirá a los desarrolladores obtener todos los productos de una Category.
Si no nos gusta cómo el diseñador a nombrado a la relación, siempre podrémos cambiarlo. Sólo hay que hacer clic en
la felcha en el diseñador, ver las propiedades y cambiar el nombre.
Retrasar la carga
LINQ to SQL permite a los desarrolladores especificar si las propiedades de las entidades deben precargarse o
retrasarse hasta el primer acceso. Podemos personalizar las reglas de precarga/retraso para las propiedades de las
entidades seleccionando cualquier propiedad o asociación en el diseñador, y en las propiedades poner la propiedad
“Delay Loaded” a true o false.
Por poner un ejemplo, imaginemos la clase entidad “Category” del modelo anterior. La tabla “Categories” de la base
de datos “NorthWind” tiene una columna “Picture” que contiene una imagen (potencialmente grande) para cada
categoría, y sólo queremos esa imagen cuando vaya a usarla (y no cuando esté haciendo una consulta para obtener
los nombres de las categorías en una lista).
Podríamos configurar la propiedad Picture para que se retrase su carga seleccionandola en el diseñador de LINQ to
SQL y en las propiedades poner “Delay Loaded” a true:
Nota: Además de configurar el significado de la precarga/retraso de las entidades, podemos sobreescribirlo vía código
cuando hagamos consultas LINQ en las clases entidad (lo veremos en el siguiente post de esta serie).
Usando procedimientos almacenados.
LINQ to SQL nos permite modelar procedimientos almacenados como métodos de nuestra clase DataContext. Por
ejemplo, supongamos que hemos definido un procedimiento almacenado simple para obtener la información de un
producto de un categoryID:
Podemos usar el server explorer de Visual Studio y arrastrar este procedimiento almacenado al diseñador de LINQ to
SQL para obtener un método fuertemente tipado que invocará a este procedimiento almacenado. Si lo arrastramos
encima de la entidad “Product” en el diseñador, el diseñador declarará que el procedimiento almacenado devuelve un
IEnumerable<Product>:
Podemos usar tanto una consulta SQL (que generará una consulta SQL adhoc) o invocar el procedimiento almacenado
añadido para obtener las entidades product de la base de datos:
Usar procedimientos almacenados para actualizar/borrar/insertar datos.
Por defecto LINQ to SQL creará automáticamente expresiones SQL apropiadas para cuando tengamos que
insertar/actualizar/borrar entidades. Por ejemplo, si escribimos el siguiente código LINQ to SQL para actualizar algunos
valores en una instancia de la entidad “Product”:
LINQ to SQL creará y ejecutará una sentencia “UPDATE” apropiada para cuando aceptemos los cambios (Veremos esto
en más profundidad en otros post).
Podemos definir procedimientos almacenados personalizados para INSERT, UPDATE, DELETE. Para configurar esto,
hacemos clic en una entidad del diseñador LINQ to SQL y en las propiedades de Delete/Insert/Update, en el botón “…”,
y ponemos un procedimiento almacenado que ya hayamos definido.
Lo curioso es que el cambio de estas propiedades se está realizando en la capa de mapeo de LINQ to SQL – lo que
implica que la actualización del código que vimos ántes sigue funcionando sin tener que hacer ninguna modificación.
Con esto libramos a los desarrolladores de que si cambiamos el modelo de datos LINQ to SQL, no tienen que tocar
ningún código para que sigua funcionando si deciden poner un procedimiento almacenado personalizado.
Resumen
LINQ to SQL provee una forma limpia de modelar las capas de datos de nuestras aplicaciones. Una vez que tengamos
nuestro modelado de datos, podemos realizar de forma eficiente consultas, inserciones, actualizaciones, y borrados
sobre él.
Con el diseñador de LINQ to SQL que viene en Visual Studio y en Visual Web Developer Express podemos crear y
administrar nuestros modelso de datos para LINQ to SQL extremadamente rápido. El diseñador LINQ to SQL también
permite una gran flexibilidad que nos permite personalizar el comportamiento por defecto y sobreescribir/extender el
sistema para que se adapte a nuestras necesidades.
En próximos post usaremos este modelo que hemos creado para ver en más detalle los procesos de consulta,
inserciones, actualizaciones y borrados. En estos post también veremos cómo añadir validaciones negocio/datos
personalizadas a las entidades que hemos diseñado.
Mike Taulty tiene una gran cantidad de videos sobre LINQ to SQL aquí, os recomiendo que los veáis. Así tenéis una
forma de aprender viendo cómo se usa LINQ to SQL.
LINQ to SQL (3ª Parte – Consultando la base de datos)
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië a 1:04 pm por Juanma
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:
Usando LINQ to SQL (1ª Parte)
LINQ to SQL (2ª Parte – Definiendo nuestras clases del modelo de datos)
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 delmé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:
Nuevas características de la nueva versión de C# Orcas
Métodos de extensión.
Expresiones Lambda
Sintaxis de consultas
Tipos anónimos
LINQ to SQL (4ª Parte) – Actualizando la base de datos
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL, Visual Studio a 5:02 pm por Juanma
En las últimas semanas he escrito una serie de post sobre LINQ to SQL. LINQ to SQL es un O/RM(object relational
mapper) integrado en la versión 3.5 del framework de .NET, y nos permite modelar fácilmente bases de datos
relacionales en clases de .NET. Podemos usar expresiones LINQ tanto para consultar la base de datos como para
actualizar, insertar y borrar datos.
Aquí tenéis los links a los tres primeros post:
Parte 1: Introducción a LINQ to SQL
Parte 2: Definiendo el modelo de datos.
Parte 3: Consultando la base de datos
En el post de hoy veremos cómo usar el modelo de datos que hemos creado, y usarlo para actualizar, insertar y borrar
datos. También veremos cómo integrar reglas de negocio y crear lógica de validación personalizada con nuetro
modelo de datos.
Modelado de la base de datos NorthWind con LINQ to SQL
En la segundo post de esta serie, vimos cómo crear el modelo de clases con el diseñador de LINQ to SQL que trae VS
2008. Aquí tenéis el modelo que creamos a partir de la base de datos de ejemplo Northwind que usaremos en este
post:
Cuando definimos el modelo definimos cinco clases: Product, Category, Customer, Order y OrderDetail. Las
propiedades de cada clase mapean las diferentes columnas de las tablas correspondientes en la base de datos. Cada
instancia de cada clase es una entidad que representa una fila de cada tabal.
Cuando definimos nuestro modelo de datos, el diseñador LINQ to SQL creó una clase llamada DataContext que
proporciona todo lo necesario para poder consultar/actualizar la base de datos. En nuestro ejemplo, esta clase se
llama NorthwindDataContext. Ésta clase tiene unas propiedades que representan cada tabla modelada de la base de
datos (en concreto: Products, Categories, Customers, Orders y OrderDetails).
Como vimos en el tercer post de esta serie, podemos usar expresiones LINQ para consultar y obtener datos usando la
clase NorthwindDataContext.LINQ to SQL traduce automáticamente estas expresiones LINQ al código SQL apropiado
en tiempo de ejecución.
Por ejemplo, la siguiente expresión devuelve un objeto Product buscando el nombre del producto:
La siguiente consulta nos devuelve todos los productos de la base de datos que no han sido pedidos, y cuyo precio es
mayor de 100 dólares:
Estamos usando la asociación “OrderDetails” de cada producto como parte de la consulta sólo para obtener aquellos
productos que no se han pedido.
Seguir los cambios y DataContext.SubmitChanges()
Cuando creamos consultas y obtenemos objetos como en los ejemplos anteriores, LINQ to SQL estará pendiente de los
cambios o actualizaciones que les hagamos a los objetos. Podemos hacer tantas consultas y cambios como queramos
usando la clase DataContext de LINQ to SQL, sabiendo que dichos cambios serán supervisados a la vez:
Nota: El seguimiento de cambios de LINQ to SQL ocurre en el lado del consumidor – y NO en la base de datos. Es decir, no estamos
consumiendo ningún recurso de la base de datos mientras lo usemos, tampoco tenemos que cambiar/instalar nada en la base de
datos para que esto funcione.
Después de realizar los cambios que queramos a los objetos que hemos obtenido con LINQ to SQL, podemos llamar al
método “SubmitChanges()” de nuestro DataContext para guardar los cambios en nuestra base de datos. Con esto,
LINQ to SQL, creara y ejecutará las sentencias SQL apropiadas para actualizar la base de datos.
Por ejemplo, el siguiente código actualiza el precio y las unidades en stock del producto “Chai” en la base de datos:
Cuando llamamos al método northwind.SubmitChanges(), LINQ to SQL creará y ejecutará las sentencias “UPDATE” de
SQL necesarias para guardar las propiedades modificadas.
Con el siguiente código iteramos sobre los productos menos populares y caros y ponemos la propiedad “ReorderLevel”
a cero.
Cuando llamamos al método northwind.SubmitChanges(), LINQ to SQL crea y ejecuta las sentencias UPDATE de SQL
necesarias para modificar los productos a los que hemos modificado la propiedad ReorderLevel.
Vemos que si no se ha modificado alguna propiedad de un Product con la asignación anterior, LINQ to SQL no
ejecutará ninguna actualización para ese objeto. Por ejemplo – si el precio del producto “Chai” era 2 dolares, y el
número de unidades en stock era cuatro, la llamada a SubmitChanges() no actualizara esos valores. Sólo los productos
cuyo ReorderLevel no era 0 se actualizarán.
Ejemplos de inserción y borrado
Además de poder actualizar la base de datos, LINQ to SQL también nos permite insertar y eliminar datos. Esto lo
conseguimos añadiendo o eliminando objectos de las colecciones disponibles en DataContest, y luego llamar al
método SubmitChanges(). LINQ to SQL “monitorizará” esas inserciones y borrados, y generará el código SQL necesario
cuando se invoque a SubmitChanges()
Añadiendo un producto
Podemos añadir un producto a la base de datos creando un nuevo objeto “Product”, inicializando sus propiedades y
añadirlo a la colección “Products” de nuestro DataContext:
Cuando llamemos a SubmitChanges() se añadirá una nueva fila en la tabla de productos.
Borrando productos
De la misma forma que añadimos un nuevo producto a la base de datos añadiendo un objeto Product a la colección
Products del DataContext, también podemos borrar productos borrándolos de esa misma colección:
Lo que estamos haciendo es obtener una secuencia de productos “alternados” de la tabla, es decir, no ordenados por
ninguna expresión LINQ, y luego esa secuencia se la pasamos al método RemoveAll() de la colección “Products”.
Cuando llamamos a SubmitChanges() todos esos productos serán borrados de la tabla
Actualizaciones y relaciones
Lo que hace que los O/RM’s como LINQ to SQL sean tan flexibles es que también nos permiten modelar las relaciones
entre las tablas. Por ejemplo, podemos modelar que cada producto tenga una categoría, que cada pedido tenga un
detalle de pedido, asociar cada detalle de pedido con un producto, y tener un conjunto de pedidos en cada cliente. Ya
vimos cómo modelar las relaciones en la segunda parte de esta serie de post.
LINQ to SQL nos permite aprovechar estas relaciones tanto para consultar como para actualizar nuestros datos. Por
ejemplo, con el siguiente código creamos un nuevo producto y lo asociamos con la categoría “Beverages”:
Estamos añadiendo el objeto producto en la colección de categorías de productos. Esto indicará que hay una relación
entre dos objetos, y hará que LINQ to SQL mantenga automáticamente las relaciones de clave primaria/ajena entre los
dos cuando llamemos a SubmitChanges().
Veamos otro ejemplo para ver cómo LINQ to SQL nos ayuda a mantener limpio el código referente a las relaciones
entre las tablas. En el siguiente ejemplo estamos creando un nuevo pedido para un cliente existente. Después de
rellenar las propiedades necesarias, podemos crear dos objetos de detalles de pedido y asociarlo a un pedido de un
cliente y actualizaremos la base de datos con todos los cambios:
Como vemos, el modelo de programación que hemos usado para hacer todo esto es realmente limpio y orientado a
objetos.
Transacciones
Una transacción es un servicio de la base de datos que garantiza que un conjunto de acciones individuales van a
suceder de forma atómica – es decir, o se pueden completar todas o si hay alguna que falle, todas las demas se
descartarán, y el estado de la base de datos será el mismo que ántes de comenzar la transacción.
Cuando llamemos a SubmitChanges(), las actualizaciones se mapean en una única transacción. Es decir, la base de
datos no tendrá nunca un estado inconsistente si hacemos muchos cambios – tanto si se hacen las actualizaciones
como si no.
Si no hay ninguna transacción en curso, el objeto DataContext empezará una transacción de la base de datos para
guardar las actualizaciones que hagamos con SubmitChanges(). Pero LINQ to SQL también nos permite definir
explícitamente y usar nuestro propio sistema de transacciones (introducido en la versión 2.0 de .NET). Esto hace más
fácil aún integrar código LINQ to SQL con el código de acceso a datos que ya tengamos. También nos permite encolar
recursos que no son propios de la base de datos en la misma transacción - por ejemplo: podemos enviar un mensage
MSMQ, actualizar el sistema de archivos (usando el nuevo soporte transaccional de sistemas de archivos), etc – y
enlazar todas estas tareas en una sola transacción a la hora de actualizar la base de datos
Validación y lógica de negocio
Una de las cosas más importantes que los desarrolladores tienen que hacer cuando trabajan con datos es incorporar
validación y reglas de negocio. LINQ to SQL tiene varias formas para hacer que los desarrolladores puedan hacer eso
de forma fácil y clara.
LINQ to SQL nos permite añadir esta validación lógica una vez. De forma que no tendremos que repetir esa lógica en
varios sitios, con lo que conseguimos un modelo de datos más mantenible y más claro.
Soporte de validación de esquemas
Cuando definimos el modelo de clases de datos con el diseñador de LINQ to SQL de VS 2008, se añadirán algunas
reglas de validación obtenidas del esquema de las tablas de la base de datos.
Los tipos de datos de las propiedades de las clases del modelo de datos coincidirán con el esquema de la base de
datos. Con esto tendremos errores de compilación si intentamos asignar un booleano a un valor decimal, o si
convertirmos tipos numéricos incorrectamente.
Si una columna en la base de datos está marcada como nullable, la propiedad correspondiente que crea el diseñador
de LINQ to SQL será un tipo nullable. Las columnas marcadas como no nullables lanzarán excepciones si no les
asignamos ningun valor. LINQ to SQL también se asegurará que de que los valores identidad/unicos se asignan
correctamente.
Obviamente podemos usar el diseñador LINQ to SQL para sobreescribir los valores por defecto del esquema si
queremos – pero por defecto, las tendremos automáticamente sin tener que hacer nada. LINQ to SQL también
comprueba los valores de los parámetros de las consultas SQL, de manera que no tendremos que preocuparnos por
los ataques de inyección de SQL.
Soporte para validación personalizada de propiedades
La validación de datos a través de esquemas es muy útil, pero no suele ser suficiente en escenarios reales.
Imaginemos que en la base de datos Northwind tenemos una propiedad “Phone” en la clase “Customer” que está
definida en la base de datos como nvarchar. Usando LINQ to SQL podemos escribir el siguiente código para
actualizarlo con un número de teléfono válido:
El problema que nos encontraríamos, sería que el siguiente código sigue siendo válido desde el punto de vista de un
esquema SQL (ya que sigue siendo una cadena, no un número de teléfono válido).
Para no permitir que no se puedan meter números de teléfono erróneos en nuestra base de datos, podemos añadir
una regla de validación personalizada a la clase Customer de nuestro modelo de datos. Es realmente fácil, todo lo que
necesitamos hacer es añadir una nueva clase parcial a nuestro proyecto que defina el siguiente método:
Este código usa dos caracteristicas de LINQ to SQL:
1. Todas las clases que genera el diseñador LINQ to SQL son “parciales” – es decir, podemos añadir métodos
adicionales, propiedades, y eventos (en archivos separados). Así podemos extender nuestro modelo de clases
creada por el diseñador de LINQ to SQL con reglas de validación y métodos auxiliares que definamos. No es
necesario ninguna configuración.
2. LINQ to SQL expone una serie de puntos de extensión en el modelo de datos que podemos usar para añadir
validación lógica. Muchos de estos puntos de extensión usan la nueva característica llamada “métodos parciales”
que viene con VB y C# en VS 2008 Beta2. Wes Dyer el equipo de C# ha escrito un post explicando cómo va esto
de los métodos parciales.
En nuestro ejemplo de validación, estamos usando el método parcial OnPhoneChangin que se ejecuta cada vez que se
cambia el valor de la propiedad ”Phone” de un objeto “Customer”. Podemos usar este método para validar la entrada
de datos (en este caso estamos usan una expresión regular). Si todo va bien, LINQ to SQL asumirá que el valor es
válido. Si hay algún problema con el valor, podemos lanzar una excepción en el método de validación – que hará que
la asignación no se haga.
Soporte para validación personalizada de objetos entidad.
En el punto anterior hemos visto cómo añadir validación a una propiedad individual de nuestro modelo de datos. Sin
embargo, algunas veces, necesitamos/queremos validar validar multiples propiedades de un objeto.
Veamos un ejemplo, tenemos un objeto Order y queremos poner las propiedades “OrderDate” y “RequiredDate”:
Este código es legal desde el punto de vista de SQL – aunque no tenga ningún sentido la propiedad de fecha de
entrega, que era para ayer.
LINQ to SQL en Beta2 nos permite añadir reglas de validación a nivel de entidad para corregir este tipo de errores.
Podemos añadir una clase parcial para nuestra entidad “Order” e implementar el método parcial OnValidate() que se
invocará ántes de que se guarden los datos en la base de datos. De esta forma, podemos acceder y validar todas las
propiedades de nuestro modelo de datos:
De esta forma podemos validar cualquiera de las propiedades de la entidad (incluso obtener acceso de sólo lectura a
los objetos asociados), y lanzar una excepción si el valor es incorrecto. Cualquier excepción lanzada desde el método
OnValidate() abortará cualquier cambio que queramos hacer en la base de datos, y deshacer todos los cambios
hechos en la transacción actual.
Validación en los métodos de inserción/actualización/borrado.
A menudo necesitamos añadir validación específica en los métodos de inserción, actualización o borrado. LINQ to SQL
nos lo permite añadiendo una clase parcial que extienda a la clase DataContext e implementar métodos parciales para
personalizar la lógica de inserción, actualización y borrado de las entidades de nuestro modelo de datos. Estos
métodos serán llamados automáticamente cuando invoquemos a SubmitChanges().
Podemos añadir la validación lógica que estimemos oportuna con estos métodos – y si todo va bien, LINQ to SQL
continará guardando los datos en la base de datos (llamando al método de DataContext “ExecuteDynamicXYZ”).
Podemos añadir métodos que se invocarán automáticamente cuando se vayan a crear/actualizar/borrar datos. Por
ejemplo, supongamos que queremos crear un nuevo pedido y asociarlo con un cliente existente:
Cuando llamamos a northwind.SubmitChanges(), LINQ to SQL determinará que es necesario guardar el nuevo objeto
Order, y ejecutará nuestro método parcial “InsertOrder”.
Avanzado: Viendo la lista de cambios de la transacción
Hay veces que no nos interesa añadir validación lógica a elementos individuales, sino que queremos ser capaces de
ver toda la lista de cambios que están ocurriendo en una transacción.
Desde la Beta2 de .NET 3.5, LINQ to SQL nos permite acceder a la lista de cambios a través del método
DataContext.GetChangeList(). Nos devolverá un objeto ChangeList que expone una serie de colecciones de adiciones,
borrados y modificaciones que se han hecho.
Una aproximación que podemos hacer en algunos escenarios es crear una clase parcial de la clase DataContext y
sobreescribir su método SubmitChange(). Podemos obtener la lista de ChangeList() para las operaciones de
actualizaciones y crear cualquier validación que queramos:
Este ejemplo es un caso de uso avanzado – pero es interesante saber que siempre podremos extender y
aprovecharnos de esta forma de las nuevas características de LINQ to SQL.
Administrando cambios simultáneos con concurrencia optimista.
Una de las cosas en las que tenemos que pensar los desarrolladores en entornos multi-usuarios es cómo administrar
las actualizaciones de los mismos datos en la base de datos. Por ejemplo, imaginemos que tenemos dos usuarios que
obtienen un objeto product, y uno de ellos cambia el ReorderLevel a 0 mientras que el otro lo pone a 1. Si ambos
usuarios guardan esos cambios en la base de datos, el desarrollador tiene que decidir cómo tratar ese conflicto.
Una solución es dejar que sea el último que lo guarda – es decir, que el valor que el primer usuario guardó se perderá
sin que éste se de cuenta. Esta es una solución muy pobre (e incorrecta).
Otra solución que permite LINQ to SQL es usar el modelo de concurrencia optimista, es decir, LINQ to SQL detectará
automáticamente si el valor original de la base de datos ha sido actualizado por alguien ántes que se guarden los
nuevos datos. LINQ to SQL nos da una lista de conflictos de valores cambiados al desarrollador y nos permite tanto
hacer lo que queramos como avisar al usuario de la aplicación para que nos indique el propio usuario lo que quiere
hacer.
Ya veremos en más detalle este tema en un próximo post.
Uso de procedimientos almacenados o lógica SQL personalizada para insertar, actualizar y borrar.
Una de las preguntas que tienen los desarrolladores (en especial los DBAs), que suelen escribir procedimientos
almacenados con SQL personalizadas, cuando ven LINQ to SQL por primeravez es: “¿pero cómo podemos tener control
absoluto del SQL que se está ejecutando?”.
Las buenas noticias son que LINQ to SQL tiene un modelo muy flexible que nos permite sobreescribir el SQL que se
está ejecutando, y llamar a los procedimientos almacenados que desarrollemos para añadir, actualizar o borrar datos.
Lo realmente increible es que podemos empezar definiendo nuestro modelo de datos y dejar que LINQ to SQL
administre las inserciones, actualizaciones y borrados. Una vez hecho esto, podemos personalizar el modelo de datos
para que use nuestros propios procedimientos almacenados o nuestras sentencias SQL – sin tener que cambiar nada
de la lógica de aplicación que estamos usando para nuestro modelo de datos, ni cambiar nada de las validaciones ni
de la lógica de negocio. Esto nos da una gran flexibilidad a la hora de construir nuestra aplicación.
Dejaremos para otro post cómo personalizar los modelos de datos con procedimientos almacenados o sentencias SQL.
Resumen.
Este post presenta un buen resumen sobre cómo podemos usar LINQ to SQL para actualizar nuestra base de datos e
integrar de una forma clara validación de datos y lógica de negocio. Creo que encontraréis que LINQ to SQL
incrementa mucho la prouctividad a la hora de trabajar con datos, y nos permite escribir código orientado a objeto
claro en el acceso a datos.
LINQ to SQL (5ª Parte) – Enlazar controles de interfaz de usuario con el ASP:LinqDatSource
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL, Visual Studio a 10:43 pm por Juanma
En las últimas semanas he escrito una serie de post sobre LINQ to SQL. LINQ to SQL es un O/RM que viene con la
versión 3.5 del framework .NET, y nos permite modelar bases de datos relacionales con clases de .NET. Podemos usar
expresiones LINQ para consultar la base de datos, así como actualizar, insertar y borrar datos.
Aquí tenéis los links a los post anteriores:
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.
En estos post hemos visto cómo podemos usar LINQ to SQL programáticamente para consultar y actualizar nuestra
base de datos.
En el post de hoy veremos el nuevo control <asp:LinqDataSource> de la nueva versión del .NET Framework (3.5). Este
control es una nueva fuente de datos para ASP.NET (como los controles ObjectDataSource y SQLDataSource de
ASP.NET 2.0) que nos va a permitir enlazar controles de la interfaz de usuario a nuestro modelo de datos LINQ to SQL.
Aplicación de ejemplo que construiremos.
El ejemplo que veremos se trata de una aplicación muy simple que nos va a permitir editar los datos de la tabla
products:
La aplicación le da al usuario las siguientes opciones de gestión:
1. Filtrado de productos por categorías.
2. Ordenar los productos haciendo clic en la cabecera de las columnas (Nombre, Precio, unidades en stock, etc)
3. Navegar en por los productos de 10 en 10.
4. Edición de los detalles de cualquier producto.
5. Borrar productos de la lista.
La aplicación web la implementaremos con un modelo de datos muy limpio creado con el ORM LINQ to SQL.
Todas las reglas de negocio y las validaciones lógicas se implementarán en el la capa de datos – y no en la capa de
presentación ni en ninguna página web. Con esto conseguiremos: 1) Un conjunto consistente de reglas de negocio que
serán usadas en toda la aplicación, 2) escribiremos menos código y mejor aún, no lo repetiremos y 3) podremos
cambiar las reglas de negocio cuando queramos sin tener que actualizar ese código en miles de sitios en toda la
aplicación.
También aprovecharemos el soporte de paginado y ordenación de LINQ to SQL para asegurarnos que tales
operaciones no se realizan en la capa intermedia, sino en la base de datos (es decir, sólo obtendremos 10 productos
de la base de datos en cada momento – no estamos obteniendo miles de filas y ordenándolas/paginándolas en el
servidor web).
¿Qué es el control <asp:LinqDataSource> y cómo nos ayuda?
El control <asp:LinqDataSource> es un control de ASP.NET que implementa elpatrón DataSourceControl que se
introdujo con ASP.NET 2.0. Es similar a los controles ObjectDataSource y SqlDataSource en que puede enlazar un
control ASP.NET en una página con la base de datos. La diferencia es que en lugar de enlazar directamente con la
base de datos (como el SqlDataSource) o a una clase genérica (como el ObjectDataSource), este control está diseñado
para enlazar aun modelo de datos con LINQ.
Una de las ventajas de usar este control es que nivela la flexibilidad de los ORMs basados en LINQ. No tenemos que
definir métodos para inserción/consulta/actualización y borrado para el datasource – sino que añadimos éste control a
nuestro modelo de datos, definimos con qué entidades queremos que trabaje, y enlazamos cualquier control de
ASP.NET para que trabaje con él.
Por ejemplo, para tener un listado básico de los productos que use las entidades Product con un modelo de datos LINQ
to SQL, sólo tenemos que declarar un control <asp:linqdatasource> en nuestra página que enlaze a la clase
datacontext de nuestro LINQ to SQL, identificar las entidades (por ejemplo: Products). Y ya podemos enlazar un
GridView con él (modificando su propiedad DataSourceID) para obtener un grid como el siguiente:
Sin tener que hacer nada más, podemos ejecutar la página y tener un listado de los productos con paginado y
ordenación. Podemos añadir los botones edit y delete en el grid y tener soporte automático para ello. No tenemos que
añadir ningún método, mapear ningún parámetro, ni escribir ningún código para el control <asp:LinqDataSource>
para tener todas estas operaciones. Cuando se hacen actualizaciones, el ORM se asegurará de que todas las reglas de
negocio y de validación que hemos añadido se cumplan ántes de guardar los datos.
Importante: La belleza de LINQ y LINQ to SQL es que obviamente no sólo sirven para escenarios como el anterior – o para casos
particulares para enlazar controles de interfaces de usuario como con el LinqDataSource. Como ya hemos visto en los post
anteriores, escribir código con este ORM es muy limpio. Siempre podemos escribir código personalizado para nuestras interfaces de
usuario que trabajen directamente con el modelo de LINQ to SQL si queremos o cuando nos encontremos en un escenario en el que
no se pueda usar <asp:linqdatasource>
En los siguientes pasos veremos como montar la aplicación que hemos descrito usando LINQ to SQL y el control
<asp:LinqDataSource>
Paso 1: Definir nuestro modelo de datos
Empezaremos definiendo el modelo de datos que usaremos para representar la base de datos.
Vimos cómo definirlo en la segunda parte de estos artículos. Aquí tenéis una captura de pantalla de las clases del
modelo de datos creado con el diseñador de LINQ to SQL de la base de datos “Northwind”:
Volveremos sobre nuestro modelo de datos en el paso 5 de este tutorial cuando añadamos algunas reglas de
validación de negocio. Pero para empezar usaremos el modelo de arriba.
Paso 2: Creación de un listado básico de productos.
Empezaremos a crear la interfaz con una página ASP.NET con un control <asp:gridview> con algun estilo css:
Podríamos escribir código para enlazar programáticamente nuestro modelo de datos al GridView (como hicimos en la
tercera parte de la serie), o podemos usar el control <asp:linqdatasource> para enlazar el GridView al modelo de
datos.
VS2008 nos permite enlazar nuestro GridView (o cualquier control de servidor ASP.NET) gráficamente a datos LINQ.
Para hacerlo tenemos que pasar a la vista de diseño, seleccionar el GridView y elegir la opción “New Data Source …”
en “Choose Data Source”:
Esto nos mostrará un cuadro de diálogo con una lista de fuentes de datos disponibles. Seleccionamos la nueva opción
“LINQ” y le damos el nombre que queramos:
El diseñador del control <asp:linqdatasource> nos mostrará las clases DataContext de LINQ to SQL que hay
disponibles (incluyendo aquellas que están en las librerías que tengamos referenciadas):
Seleccionaremos el modelo que creamos con el diseñador de LINQ to SQL. Seleccionaremos la tabla que queramos
que sea la entidad principal con la que enlazar el grid. En nuestro caso seleccionaremos la clase Products. También
seleccionamos el boton “Advanced” y habilitaremos las actualizaciones y borrados para la fuente de datos:
Cuando hagamos clic en el botón “Finish”, VS 2008 declarará un contorl <asp:linqdatasource> en el .aspx y
actualizará el gridview para que use esta fuente de datos. También generará las columnas necesarias para los
diferentes campos de la clase Product:
Ahora desplegamos el “smart task” del grid view y le indicamos que queremos habilitar la paginación, ordenado,
edición y borrado:
Podemos pular F5 para ejecutar la aplicación, y veremos una página con el listado de productos con paginación y
ordenado:
También podemos pulsar los botones “edit” o “delete” en cada fila para actualizar los datos:
Si pasamos a la vista de código de la página, veremos las marcas que contiene. El control <asp:linqdatasource>
apunta a la clase DataContext, y a la tabla que le dijimos al principio. El GridView tiene como fuente de datos el
control <asp:linqdatasource> (en el DatasourceID) y le indica qué columnas tienen que incluirse en el grid, cuál es el
texto de la cabecera, y cual es la expresión de ordenación:
Ahora tenemos lo básico para empezar a trabajar con el comportamiento de esta interfaz de usuario.
Paso 3: Limpiando las columnas.
El grid tiene muchas columnas, y dos de ellas (el SupplierId y el CategoryID) son claves ajenas, que no conviene
mostrar al usuario.
Eliminando columnas innecesarias
Empezaremos eliminando alguna de las columnas que no necesitamos. Podemos hacer esto en la vista de
código(borrando la declaración de <asp:boundfield> correspondiente) o en la vista de diseño (haciendo clic en la
columna y seleccionando la tarea “remove”). Por ejemplo, podemos eliminar la columna “QuantityPerUnit” y ejectuar
la aplicación:
Si hemos usado el contorl <asp:ObjectDataSource> y le hemos indicado explícitamente los parámetros de
actualización (update) a los métodos de actualización (por defecto cuando usamos un DataSet basado en
TableAdapters) una de las cosas que tenemos que hacer es cambiar la firma de los métodos de actualización del
TableAdapter. Por ejemplo: si eliminamos una columnan del grid, tenemos que modificar el TableAdapter para que
soporte los métodos de actualización sin parámetros.
Con el control <asp:LinqDataSource> es que no tendríamos que hacer este tipo de cambios. Simplemente borrando (o
añadiendo) una columna a la interfaz de usuario y ejecutamos la aplicación -no hacen falta más cambios. Con esto
conseguimos que los cambios que se hagan en la interfaz de usuario sean mucho más fáciles, y nos permite
iteraciones más rápidas en nuestra aplicación.
Limpiando las columnas SupplierId y CategoryID
Hasta ahora estamos mostrando valores enteros de las claves ajenas en nuestro GridView para los campos Supplier y
Category:
Desde el punto de vista del modelo de datos es correcto, pero no desde el punto de vista del usuario. Lo que
queremos hacer es mostrar el nombre de la categoría y el nombre del proveedor, y mostrar una lista desplegable en
modo edición para que podamos cambiar estas asociaciones de forma fácil.
Podemos cambiar el gridview para que muestre el nombre del proveedor y el de la categoría en lugar del ID,
cambiando el <asp:BoundField> por un <asp:TemplateField>. Y en este templateField añadimos el contenido que
queramos para personalizar la vista de la columna.
En el código siguiente aprovecharemos la capacidad de LINQ to SQL, que modeló este campo como una propiedad. Es
decir, posdemos enlazar de forma fácil las propiedades Supplier.CompanyName y Category.CategoryName al grid:
Ahora ejecutamos la aplicación y tenedremos los valores de Category y Supplier de la siguiente forma:
Para tener una lista desplegable en estos dos campos en el modo edición, tenemos que añadir dos controles
<asp:LinqDataSource> a la página. Los configuraremos para que se enlacen a las entidades Categories y Suppliers del
modelo de datos creado por LINQ to SQL:
Ahora volvemos a la columnas <asp:TemplateField> que añadimos antes y personalizaremos la parte de edición
(especificando un EditItemTemplate). De forma que tendremos una lista desplegable en la vista edición, donde los
valores disponibles se obtendrán de las fuentes de datos de categorías y proveedores, y enlazaremos doblemente el
valor seleccionado al SupplierId y CategoryID de las claves ajenas:
Y ahora, cuando el usuario haga clic en el botón de edición, se mostrará una lista con todos los proveedores
disponibles:
Y cuando salvemos los cambios se guardará el valor seleccionado en la lista desplegable.
Paso 4: Filtrando el listado de productos.
Más que mostrar todos los productos de la base de datos, podemos actualizar la interfaz de usuario para incluir una
lista desplegable que permita al usuario filtrar los productos por una categoría particular.
Como ya añadimos un <asp:LinqDataSource> enlazada a las Categorías de nuesto modelo de LINQ to SQL, sólo
tendremos que crear un nuevo control de lista desplegable y enlazarlo. Por ejemplo:
Cuando ejecutemos la página tendremos un filtro con todas las categorías al principio de la página:
Lo último que queda es que cuando seleccionemos una categoría se muestren los productos de dicha categoría en el
gridview. Lo más facil es seleccionar la opción de “Configure DataSource” en el smart task del GridView:
Con esto veremos el cuadro de dialogo para configurar el <asp:LinqDataSource> que usamos al principio del tutorial.
Seleccionamos el botón “Where” para añadir un campo de filtro al datasource. Podemos añadir cualquier número de
expresiones, y declarativamente asignar los valores con los que filtrar (por ejemplo: de una querystring, de valores de
formulario, de cualquier control en la página, etc).
Vamos a poner un filtro de productos por su CategoryID, y obtenemos el valor con el que filtrar de la lista desplegable
que creamos antes:
Cuando le damos a “Finish”, el contorl <asp:linqdatasource> se actualizará para reflejar los cambios de filtrado:
Y cuando ejecutamos la aplicación el usuario será capaz de elegir una categoría en la lista de filtrado y paginar,
ordenar, editar y borrar los productos de una categoría:
El control <asp:LinqDataSource> aplicará las expresiones LINQ necesarias para que cuando se ejecute contra el
modelo de datos LINQ to SQL sólo se obtengan los datos necesarios de la base de datos (por ejemplo: en el grid
anterior sólo obtendremos 3 filas con los productos de la categoría Confection).
También podemos gestionar nosotros el evento Selecting si queremos escribir expresiones LINQ personalizadas.
Paso 5: Añadir reglas de validación de negocio
Como ya vimos en la cuarta parte de esta serie de post, cuando definimos modelos de datos con LINQ to SQL
tendremos un conjunto de esquemas de validación por defecto en el modelo de clases. Es decir, si intentamos meter
un valor nulo en una columna requerida, intentar asignar un string a un entero, o asignar una valor de clave ajena en
una fila que no exista, el modelo lanzará un error y se asegurará de que la integrar de la base de datos se mantiene.
El esquema básico de validación es sólo el primer paso, y es muy raro en la mayoría de aplicaciones reales.
Normalmente necesitaremos añadir reglas adicionales a nuestro modelo de datos. Afortunadamente con LINQ to SQL
podemos añadir estas reglas de forma muy fácil (para más detalles sobre este tema, leed la cuarta parte de esta serie
de post).
Ejemplo de escenario con reglas de validación
Por ejemplo, supongamos una regla básica que queramos reforzar. Queremos que un usuario de nuestra aplicación no
pueda interrumpir un producto mientras haya unidades pedidas:
Si un usuario intenta guardar la fila anterior, queremos prevenir que ese cambio se guarde en la base de datos y que
genere un error para que el usuario lo arregle.
Añadiendo una regla de validación al modelo de datos.
El sitio incorrecto para añadir este tipo de validación es en la capa de presentación. Añadirlo en esta capa implica que
esta regla será sólo válida en un lugar concreto, y no será automático en cualquier parte de la aplicación. Distribuir la
lógica de negocio en la capa de presentación también hará que nuestra vida sea realmente penosa a medida que
nuestra aplicación crezca – ya que cualquier cambio/actualización en nuestro negocio hara necesarios cambios de
codigo en todas partes.
El lugar correcto para este tipo de validación es en las clases del modelo de datos de LINQ to SQL. Como ya vimos en
la cuarta parte de esta serie, todas las clases se generan por el diseñador de LINQ to SQL como clases parciales – con
lo que podemos añadir métodos/eventos/propiedades fácilmente. El modelo de LINQ to SQL ejectuará los métodos de
validación que podemos implementar.
Por ejemplo, Podemos añadir una clase parcial Product a nuestro proyecto que implemente el método parcial
OnValidate() que LINQ to SQL llama antes de guardar las entidades de Product. En este método podemos añadir la
siguiente regla de negocio para segurarnos que los productos no pueden tener un Reorder Level si el producto es
discontinued:
Una vez que añadimos la clase anterior al proyecto LINQ to SQL, la regla anterior se comprobará cada vez que alguien
use nuestro modelo de datos e intente modificar la base de datos. Esto se hace tanto para los productos existentes
que se vayan a actualizar como para los que se vayan a añadir nuevos.
Como el <asp:LinqDAtaSource> trabaja contra nuestro modelo de datos, cada actualización/inserción/borrado pasará
por esta regla de validación antes de guardar los cambios. No necesitamos hacer nada más en la capa de
presentación para que se cumpla esta regla – se comprobará cada vez que se use nuestro modelo de datos.
Añadir un manejador de errores en la capa de presentación.
Por defecto si un usuario usa nuestro GridView para meter una combinación no válida de UnitOnOrder/Discontinued,
nuestro modelo LINQ to SQL lanzará una excepción. El <asp:LinqDataSource> capturará la excepción y nos
proporciona un evento que podemos usar para controlarlo. Si nadie usa el evento, el contol GridView (u otro) enlazado
al <asp:LinqDataSource> capturará el error y proveerá un evento para controlarlo. Si nadie controla el error será
pasado al manejador d ela página, y si nadie lo controla, se le pasará al evento Application_Error() en el archivo
Global.asax. Los desarrolladores pueden hacer esto en cualquier paso del camino para añadir la lógica de errores que
queramos en la capa de presentación.
Para la aplicación que estamos viendo, seguramente el mejor lugar para controlar cualquier error de actualización sea
en el vento rowUpdated del gridView. Este evento se ejectuará cada vez que se actualize en nuestro datasource, y
podemos ver los detalles de la excepción si falla la actualización. Podemos añadir el siguiente código para comprobar
si ha ocurrido un error, y mostrar un error adecuado en caso de que ocurra:
No hemos tenido que añadir ninguna validación lógica en nuestra interfaz de usuario. En lugar de eso, estamos
obteniendo el error que lanzamos en nuestra lógica de negocio y la estamos usando para mostrar un mensaje
adecuado al usuario (Estamos mostrando un error más genérico).
También le estamos indicando que queramos que el GridView se mantenga en el modo edición cuando ocurra un error
– de forma que podemos evitar que el usuario pierda los cambios que estaba haciendo, y modificar los valores y darle
a “update” otra vez e intentar guardarlo. Podemos añadir un control <asp:literal> en el “ErrorMessage” en cualquier
parte de la pagina que queramos para controlar donde queremos que se muestre el error:
Y ahora cuando intentemos actualizar un producto con valores erroneos veremos un mensaje de error que indica cómo
arreglarlo:
Lo bueno de esto es que podemos añadir o cambiar las reglas de negocio sin tener que cambiar nada en la capa de
presentación. Las reglas de validación, y sus mensajes correspondientes, pueden centralizarse en un lugar en concreto
del modelo de datos y se aplicarán en todas partes.
Resumen
El control <asp:LinqDataSource> nos da una forma fácil de enlazar controles de ASP.NET a nuestro modelo de LINQ to
SQL. Permite obtener/actualizar/insertar/borrar datos del modelo de datos.
En nuestra aplicación hemos usado el ORM LINQ to SQL para crear un modelo limpio, orientado a objetos. Añadimos
tres contorles ASP.NET a la página (un gridView, una lista desplegable, y un errormessage literal), y hemos añadido
tres contorles <asp:LinqDataSource> para enlazar a Product, Category y Proveedores:
Escribimos 5 líneas de validación lógica y 11 lineas para la gestión de errores.
El resultado final es una aplicación web simple que permite a los usuarios filtrar los productos por su categoría,
ordenar y paginar eficientemente dichos productos, editar los productos y guardarlos (con nuestra reglas de negocio),
y borrar productos del sistema (también con nuestra lógica de negocio).
LINQ to SQL (Parte 6 – Obtener datos con procedimientos almacenados)
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL a 3:07 pm por Juanma
En las últimas semanas he escrito una serie de post sobre LINQ to SQL. Es un ORM integrado en .NET 3.5, y nos
permite modelar bases de datos relacionales con clases de .NET. Podemos usar expresiones LINQ para consultar a la
base de datos, actualiazarla, insertar y borrar datos.
Aquí tenéis los enlaces a los otros post:
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
En estos posts vimos cómo usar expresiones LINQ para obtener programáticamente datos de la base de datos.
En el post de hoy veremos cómo podemos usar los procedimientos almacenados (SPROCs) y las funciones definidas
por el usuario (UDFs) con nuestro modelo LINQ to SQL. El post de hoy veremos el caso de los SPROCs para consultar y
obtener datos de la base de datos. En el siguiente post de esta serie veremos cómo actualizar/insertar/borrar datos
con SPROCs.
¿SPROC o no SPROC? Esa es la cuestión
La pregunta sobre cuando usar el SQL dinámico generado por un ORM en lugar de procedimientos almacenados
creando una capa de datos es causa de debates muy acalorados entre desarrolladores, arquitectos y DBAs. Mucha
gente más lista que yo ha escrito sobre esto, así que no me decantaré ni por un lado ni por otro.
LINQ to SQL es muy flexible, y puede usare para crear un modelo de datos cuyos objetos sean independientes del
esquema de la base de datos, y puedeencapsular lógica de negocio y reglas de validación que funcionan tanto si se
usa SQL generado dinámicamente o a través de SPROCs.
En el tercer post de esta serie, hablamos sobre cómo podemos escribir expresiones LINQ contra el modelo de LINQ to
SQL como el siguiente código:
Cuando escribimos expresiones LINQ como esta, LINQ to SQL ejecutará el SQL dinámico necesario para obtener los
objetos de Product que cumplan las restricciones.
Como aprenderemos en este post, también podemos mapear SPROCs en la base de datos con la clase DataContext
generada por LINQ to SQL, que nos permitirá obtener los mismo objetos de Product llamando a un procedimiento
almacenado:
Esta habilidad de poder usar tanto SQL dinámico como SPROCs con una capa de datos limpia es muy útil y nos
permite una gran flexibilidad en nuestros proyectos.
Pasos para mapear y llamar a SPROC con LINQ to SQL
En el segundo post de la serie vimos cómo usar el diseñador LINQ to SQL para crear el siguiente modelo de clases:
Fijaos en las dos partes del diseñador. La de la izquierda nos permite definir el modelo de datos que mapeara nuestra
base de datos. El de la derecha nos permite mapear SPROCs (y UDFs) en nuestro objeto DataContext, que podemos
usar en lugar del SQL dinámico para trabajar con los objetos de nuestro modelo de datos.
Cómo mapear un SPROC en un DataContext de LINQ to SQL
Para mapear SPROCs en la clase DataContext, vamos primero al explorador de servidores de VS 2008 y miramos a los
SPROCs de nuestra base de datos:
Haciendo doble clic en cualquier SPROC se abrirá para edición y podremos ver el código. Por ejemplo, aquí tenéis el
SPROC “CustOrderHist” de la base de datos Northwind:
Para mapearlo en nuestra clase DataContext, lo arrastarmos y soltamos desde el explorador de servidores al
diseñador de LINQ to SQL. Automáticamente se creará un nuevo método en la clase DataContext:
Por defecto el nombre del nuevo método en la clase DataContext será el mismo que el del SPROC, y el tipo de datos
devueltos se creará automáticamente con el siguiente patron: “[NombredelSPROC]Result”. Por ejemplo: el SPROC de
arriba devolverá una secuencia de objetos del tipo “CustOrderHistResult”. Podemos cambiar el nombre del método
seleccionándolo en el diseñador y cambiarlo en la ventana de propiedades.
Como llamar a un nuevo SPROC mapeado.
Una vez que hemos seguido los pasos para mapear el SPROC en la clase DataContext, es muy fácil de usar. Todo lo
que tenemos que hacer es llamarlo para obtener los resultados fuertemente tipados:
En VB:
En C#:
Además de poder hacer un bucle sobre los resultados, también podemos enlazar los resultados con cualquier control
para mostrarlos. Por ejemplo, el siguiente código enlaza los resultados del SPROC a un control <asp:gridview>
Con lo que mostramos la historia de productos de un cliente:
Mapeando los tipos resultado de los SPROC del modelo de datos
En el SPROC “CustOrderHist” devolvía una secuencia de objetos con dos columnas: el nombre del producto y el
numero total de pedidos que el cliente ha hecho de ese producto. El diseñador LINQ to SQL definió la clase
“CustOrderHistResult” para representar los resultados.
También podemos decidir mapear los resultados del SPROC a una clase de nuestro modelo de datos (por ejemplo: a
una entidad Product o Order).
Por ejemplo, tenemos el SPROC “GetProductsByCategory” en nuestra base de datos que devuelve la siguiente
información:
Como ántes podemos crear un método “GetProductsByCategory” en la clase DataContext que llama a este SPROC
arrastrándolo al diseñador de LINQ to SQL. Más que simplemente arrastrar el SPROC al diseñador, lo arrastraremos
encima de la clase “Product”:
Con esto, el método “GetProductsByCategory” devolverá una secuencia de objetos “Product”:
LINQ to SQL seguirá los cambios hechos a los objetos que se devuelvan como si fuesen objetos Products obtenidos a
partir de expresiones LINQ. Cuando llamemos al método “SubmitChanges()” todos los cambios hechos a esos objetos
se guardarán en la base de datos.
Por ejemplo, con el siguiente código obtenemos y cambiamos el precio de todos los productos de una categoría
aumentándolo en un 90 %:
Para entender cómo funciona el método SubmitChanges() y el seguimiento que se hace de los cambios, y ver cómo
podemos añadir lógica de negocio a nuestro modelo de datos leed el cuarto post de esta serie.
En el próximo post de esta serie veremos también cómo cambiar el SQL generado cuando
insertamos/actualizamos/borramos datos con SPROCs personalizados. Lo bueno de todo esto es que el código anterior
no habrá que cambiarlo si hemos configurado la clase DataContext para que use SPROCs para las actualizaciones -
Manejando resultados múltiples desde SPROCs
Cuando un procedimiento almacenado puede devolver varios tipos de datos, el tipo de resultado del SPROC en la clase
DataContext no puede ser fuertemente tipado. Por ejemplo, imaginemos el siguiente SPROC que puede devolver un
producto o un pedido dependiendo del parámetro de entrada:
LINQ to SQL permite crear métodos auxiliares para devolver Product o Order añadiendo una clase parcial
“NorthwindDataContext” al proyecto que defina un método (que en este caso llamaremos “VariablesShapeSample”)
que invoca al SPROC y devuelve un objeto IMultipleResult:
VB:
C#:
Una vez que añadimos este método al proyecto podemos llamarlo y convetir los resultados tanto a una secuencia de
Product como de Order:
VB:
C#:
Soporte de funciones definidas por el usuario (UDFs)
Además de SPROCS, LINQ to SQL también soporta tanto funciones de usuario de valores y de tablas de valores (UDFs).
Una vez que añadimos un método a la clase DataContext, podemos usar estas funciones en nuestras consultas LINQ.
Por ejemplo, veamos la función simple “MyUpperFunction”:
Podemos arrastrar y soltar desde el explorador de servidores al diseñador de LINQ to SQL para añadirlo como un
método a nuestro DataContext:
Luego podemos usar esta función UDF en expresiones LINQ cuando escribimos consultas contra nuestro modelo LINQ
to SQL:
VB:
C#:
Si usamos el visualizador de debug de LINQ to SQL del que ya hablamos aquí,podemos ver cómo LINQ to SQL
transforma la expresión anterior en una SQL que ejecutará el UDF en la base de datos en tiempo de ejecución:
Resumen
LINQ to SQL soporta poder usar procedimientos almacenados y UDFs contra la base de datos y los integra en nuestro
modelo de datos. En este post hemos visto cómo podemos usar SPROCs para obtener datos y pasarlo entre nuestras
clases del modelo. En el próximo post veremos cómo podemos usar SPROCS para sobreescribir la lógica de
actualización/inserción/borrado cuando llamamos a SubmitChanges() en el DataContext para guardar los cambios.
LINQ to SLQ (Parte 7 – Actualizando la base de datos con procedimientos almacenados)
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL, Visual Studio a 12:51 am por Juanma
En las últimas semanas he escrito una serie de post sobre LINQ to SQL. Es un ORM integrado en .NET 3.5, y nos
permite modelar bases de datos relacionales con clases de .NET. Podemos usar expresiones LINQ para consultar a la
base de datos, actualiazarla, insertar y borrar datos.
Aquí tenéis los enlaces a los otros post:
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 .
En la sexta parte vimos cómo podemos usar procedimientos almacenados (SPROCs) y funciones definidas por el
usuario (UDFs) para consultar la base de datos con el modelo de datos de LINQ to SQL. En el post de hoy veremos
cómo podemos usar los SPROCs para actualizar/insertar/borrar datos de nuestra base de datos.
Para ayudar a entender esto empezaremos costruyendo una capa de datos para la base de datos de ejemplo
Northwind:
Paso 1: Crear nuestra capa de acceso a datos (sin SPROCs)
En la segunda parte de esta serie vimos cómo usar el diseñador de LINQ to SQL de VS 2008 para crear el siguiente
modelo de clases:
Añadiendo reglas de validación a nuestro modelo de clases.
Después de definir nuestro modelo querremos añadir reglas de validación a nuestro modelo de datos. Podemos hacer
esto añadiendo clases parciales a nuestro proyecto y añadir las reglas de validación en esas clases (vimos cómo hacer
esto en la cuarta parte de esta serie).
Por ejemplo, podemos añadir la lógica necesaria para asegurarnos de que el número de teléfono de los clientes siguen
un patrón válido, y otra para asegurarnos de que la fecha de entrega (RequierdDate) es posterior a la fecha actual del
pedido (OrderDate). Una vez que hemos definido las clases parciales, estos métodos de validación se ejecutarán cada
vez que escribamos código para actualizar nuestros objetos de datos de nuestra aplicación:
VB:
C#:
Añadir un método de ayuda GetCustomer() a nuestro DataContext
Una vez que hemos creado nuestro modelo de clases, y que le hemos añadido reglas de validación, podemos
consultar e interactuar con los datos. Podemos hacer esto escribiendo expresiones LINQ sobre nuestro modelo de
clases (vimos cómo hacer esto en la tercera parte de esta serie). También podemos mapear SPROCs en nuestro
DataContext (esto lo vimos en la sexta parte de la serie).
Cuando creamos una capa de datos con LINQ to SQL normalmente querremos encapsular consultas comunes de LINQ
(o SPROCs) en métodos auxiliares que añadiremos a la clase DataContext. Esto lo conseguimos añadiendo una clase
parcial a nuestro proyecto. Por ejemplo, podemos añadir un método llamado “GetCustomer()” que nos permita buscar
y obtener objetos Customer de la base de datos a partir del valor CustomerID:
VB:
C#:
Paso 2: Usando nuestra capa de datos (seguimos sin SPROCs)
Ya tenemos una capa de datos que encapsula nuestro modelo de datos, integra reglas de validación, y nos permite
consultar, actualizar, insertar y borrar datos.
Veamos ahora un escenario simple usándolo para obtener un objeto customer existente, actualizamos el ContactName
y el PhoneNumber, y creamos un nuevo objeto Order para asociarlos. El siguiente código hace todo eso en una sola
transacción. LINQ to SQL se asegura de que las reglas de validación se cumplen ántes de guardar nada en la base de
datos:
VB:
C#:
LINQ to SQL monitoriza todas las modificaciones de los objetos que hemos obtenido de la base de datos, y guarda los
objetos que añadimos. Cuando llamamos al método DataContext.SubmitChanges(), LINQ to SQL comprueba las reglas
que hemos establecido, y genera automáticamente la SQL que actualizará el registro de Customer e insertará un
nuevo registro en la tabla Orders
Un momento – Pensaba que este post iba sobre SPROCs
Si aún estais leyendo, os preguntaréis dónde están los SPROCs en este post. ¿Porque os estoy mostrando el código de
arriba que hace que se genere una SQL dinámica? ¿Por qué no os he enseñado cómo llamar a un SPROC para hacer
las inserciones/actualizaciones/borrados todavía?
La razón es que el modelo de programación de LINQ to SQL tanto para trabajar con objetos modelados mediante
SPROC es exactamente el mismo que con SQL dinámico. La manera en que añadimos validación lógica es
exactamente igual (así que todas las reglas que hemos añadido a nuestro modelo de datos se aplicarán también si
usamos SPROCs). El código anterior que hemos usado para obtener un cliente, actualizarlo y añadir un nuevo pedido
es exactamente igual tanto si usamos SQL dinámico como si usamos SPROCs.
Esta simetría en el modelo de programación es muy potente ya que no tenemos que aprender dos maneras diferentes
de hacer las cosas, ni tenemos que decidir al principio del proyecto qué técnica usar, si SPROC o no. Podemos
empezar usando el SQL dinámico que nos da LINQ to SQL para las consultas, inserciones, actualizaciones y borrados.
Podemos añadir reglas de validación a nuestro modelo. Y luego podemos actualizar el modelo de datos para usar
SPROCs – o no. El código y los test que escribamos contra las clases del modelo de datos serán exáctamente iguales.
De ahora en adelante veremos cómo podemos actualizar nuestro modelo de datos usando SPROCs para
actualizar/insertar/borrar – mientras seguimos usando las mismas reglas de validación y trabajaremos con los mismos
códigos anteriores.
Cómo usar SPROCs en inserciones, actualizaciones y borrados
Podemos modificar la capa de datos que estamos construyendo para que use SPROCs, en lugar de SQL dinámico de
dos maneras:
1. Usando el diseñador de LINQ to SQL para configurar gráficamente la ejecución de los SPROCs en las diferentes
operaciones o
2. Añadir una clase parcial NorthwindDataContext a nuestro proyecto, y entonces implementar los métodos
necesarios para la inserción, borrado y actualización. (por ejemplo: InsertOrder, UpdateOrder, DeleteOrder) que
serán llamados cuando se realize alguna de las operaciones asociadas. Estos métodos parciales serán pasados a
las instancias del modelo de datos que queramos actualizar, y podemos ejecutar tanto SPROC como código SQL
para guardarlo.
Cuando usemos la primera aproximación para configurar gráficamente los SPROCs que llamaremos, por debajo se está
generando el mismo código (en clases parciales que crea él solo) que escribiríamos si elegimos la segunda opción. En
general os recomiendo que uséis el diseñador de LINQ to SQL para configurar los SPROCs en el 90% de los casos – y
crear las llamadas personalizadas a procedimientos almacenados en escenarios más avanzados.
Paso 3: Hacer otras inserciones con un SPROC
Empezaremos cambiando nuestro modelo de datos para que use SPROCs con el objeto Order.
Primero nos vamos a la ventana de “Explorador de Servidores” (Server Explorer) de Visual Studio, expandimos el nodo
“Stored Procedures” de nuestra base de datos, hacemos clic con el botón derecho y elegimos la opción “Add New
Stored Procedure”:
Creamos el nuevo procedimiento almacenado que llamaremos “InsertOrder” que añade una nueva fila order a la tabla
Orders:
Fijáos que hemos definido el parámetro “OrderId” como un parámetro de salida. ESto es debido a que la columna
OrderID es una columna identidad que se autoincrementa cada vez que se añade un nuevo registro. Quien llame a
este SPROC deverá pasarle un valor null en ese parámetro – y el SPROC devolverá en ese parámetro el nuevo valor
OrderID (llamando a la función SCOPE_IDENTITY() al final del SPROC).
Después de crear el SPROC abrimos el diseñador de LINQ to SQL. De la misma forma que vimos en la sexta parte de
esta serie, podemos arrastrar y soltar SPROCs desde la ventana “server explorer” al diseñador. Esto es lo que haremos
con el nuevo SPROC que acabamos de crear:
El último paso será decirle a nuestra capa de datos que use el SPROC InsertOrder cuano inserter un nuevo objeto
Order en la base de datos. Esto lo hacemos seleccionando la clase “Order” del diseñador LINQ to SQL, y en las
propiedades clicamos el botón “…” del método Insert:
Hacemos clic en el botón “…” y aparecerá una ventana que nos permite personalizar las operaciones de inserción:
Fijaos cómo el modo po defecto (“Use Runtime”) está configurado para usar LINQ to SQL como generador dinámico de
las SQL. Para cambiarlo seleccionamos el radio buton “Customize” y seleccionamos el SPROC InsertOrder de la lista de
SPROCS disponibles:
El diseñador de LINQ to SQL calculará una lista de parametros para el SPROC que hemos seleccionado, permitiéndonos
mapear las propiedades de nuestra clase Order a los parámetros del SPROC InsertOrder. Por defecto seleccionará el
que más se parezca en el nombre. Podemos cambiarlo si queremos.
Una vez que cliquemos en OK está listo. Ahora cada vez que añadamos un nuevo pedido a nuestro DataContext e
invoquemos al método SubmitChanges(), se ejecutará el SPROC InsertOrder.
Importante: Aunque estemos usando SPROC para la persistencia, el método parcial “OnValidate()” que creamos (en la
primer parte de esta serie) para encapsular las reglas de validación para los pedidos seguirán ejecutándose antes de
realizar cualquier cambio. Es decir, tenemos una forma limpia de encapsular la lógica de negocio y las reglas de
validación en nuestros modelos de datos, y podemos reutilizarlos tanto si usamos SQL o SPROCS.
Paso 4: Actualizando los clientes con SPROCs.
Ahora vamos a modificar el objeto Customer para manejar las actualizaciones con un SPROC.
Empezamos creando el SPROC “UpdateCustomer”:
Fijaos que además de pasar el parámetro @CustomerID, también tenemos un parámetro @Original_CustomerID. La
columna CustomerID de la tabla Customers no es un campo autoincremental, y puede modificarse cuando hagamos
una actualización. Por tanto necesitamos ser capaces de decirle al SPROC cual es el CustomerID original y el nuevo
CustomerID. Vamos a ver cómo mapeamos esto con el diseñador de LINQ to SQL.
Veréis que estamos pasando un parámetro llamado @Version (que es una marca de tiempo) al SPROC. Es una nueva
columna que he añadido a la tabla Customers para ayudarme a controlar la concurrencia optimista. Veremos en más
detalle este tema en otro post de esta serie – pero en resumen es que LINQ to SQL soporta completamente la
concurrencia optimista, y nos permite usar tanto una marca de tiempo o usar valores original/nuevo para detectar si
ha habido algún cambio por parte de otro usuario ántes de guardar los datos. Para este ejemplo usaremos una marca
de tiempo ya que hace que el código sea mucho más claro.
Una vez que tenemos nuestro SPROC, lo arrastramos y soltamos al diseñador LINQ to SQL para añadirlo como método
a nuestro DataContext. Seleccionamos la clase Customer y hacemos clic en el botón “…” de la propiedad Update:
Seleccionamos el radio button “Customize” y seleccionamos el SPROC UpdateCustomer:
Cuando mapeamos las propiedades de los objetos Customer con los parámetros del SPROC, veremos que tenemos que
decidir si poner la propiedad “current” en el objeto de datos, o si poner el valor original que estaba en la base de datos
antes de obtener el objeto. Por ejemplo, tendremos que asegurarnos de que mapeamos el valor “current” de la
propiedad CustomerID en el parámetro @CustomerID, y el valor original en el parámetro @original_customerID.
Cuando hacemos clic en OK ya esta terminado. Ahora cuando actualizemos cualquier cliente y llamemos a
SubmitChanges() se ejectuará el SPROC UpdateCustomer en lugar de ejecutarse un SQL dinámico.
Importante: Aunque ahora estemos usando SPROC, el método parcial “OnPhoneChanging()” de la clase Customer (que
creamos en el primer post de esta serie) para validar los números de teléfono se seguirá ejecutando de la misma
manera ántes de que se guarden los cambios. Tenemos de esta forma una forma limpia de encapsular reglas de
negocio y validación a nuestros modelos de datos, y podemos reutilizarlos tanto si usamos SQL dinámico o SPROCs.
Paso 5: Usando el modelo de datos otra vez (esta vez con SPROCs)
Ahora que ya tenemos configurada nuestra capa de datos para usar SPOCs en lugar de SQL dinámico, podemos
ejecutar el mismo código que vimos en el paso 2:
Ahora las actualizacion del objeto Customer, y la inserción del objeto ORder, se están ejecutando a través de SPROCs
en lugar de SQL dinámico. La lógica de validación que definimos se siguen ejecutando como antes, y el código sigue
siendo exactamente el mismo.
Apuntes avanzados cuando usamos SPROCs
Veamos unas cuantas recomendaciones útiles para escenarios con SPROC más avanzados con LINQ to SQL
Uso de parámetros de salida
En casos de inserción (Paso 3) hemos visto cómo podemos devolver el nuevo valor OrderID (que es un valor
identidad y autoincremental de la tabla Orders) usando un parámetro de salida en el SPROC. No estamos limitados a
devolver sólo valores de columnas identidad con SPROCs y LINQ to SQL – en realidad podemos actualizar y devolver
cualquier parámetro. Podemos usarlo tanto para insetar como para actualizar. LINQ to SQL tomará el valor resultado y
actualizará la propiedad asociada en el modelo de dato sin que tengamos que hacer ninguna consulta extra para
refrescarlo o calcularlo de nuevo.
¿Que pasa si el SPROC da un error?
Si el SPROC da un error mientras inserta, actualiza o borra un dato, LINQ to SQL cancelará y deshará la transacción de
todos los cambios asociados a la llamada SubmitChanges(). De manera que nos aseguramos la consistencia de los
datos.
¿Podemos escribir código en lugar de usar el diseñador para llamar a un SPROC?
Como ya comenté al principio, podemos usar tanto el diseñador de LINQ to SQL para mapear las operaciones con
SPROC o podemos añadir métodos parciales a la clase DataContext programáticamente e invocarlos nosotros mismo.
Aquí tenéis un ejemplo del código que deberíamos escribir para sobreescribir el método UpdateCustomer de la clase
NorthwindDataContext:
Este código es el que fué generado con el diseñador de LINQ to SQL cuando lo usamos para mapear el SPROC y
asociarlo a la operación de Update del objeto Customer. Podemos usarlo como un punto de partida y añadir alguna
lógica adicional para hacerlo más personalizado (por ejemplo: usar el valor de retorno del SPROC para lanzar
excepciones personalizadas).
Resumen
LINQ to SQL es un ORM muy flexible. Nos permite escribir código limpio orientado a objetos para obtener, acutalizar e
insertar datos.
Lo mejor de todo es que nos permite diseñar una capa de datos realmente limpia e independiente de cómo se guardan
y cargan los datos de la base de datos. Podemos usar SQL dinámico o SPROCs para esas operaciones. Lo mejor es que
el código que use nuestra capa de datos, y todas las reglas de negocio asociadas, serán las mismas sin importar que
método de persistencia estemos usando.
LINQ to SQL (Parte 8 – Ejecutar consultas SQL personalizadas)
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL, Visual Studio a 9:48 pm por Juanma
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í teneis 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.
En los dos últimos post vismo cómo podemos usar los procedimientos almacenados de nuestra base de datos para
consultar, insertar, actualizar y borrar datos con el modelo de LINQ to SQL.
Una pregunta que me han hecho mucho desde que he escrito estos post es: ¿que pasa si quiero control total sobre las
consultas SQL que usa LINQ to SQL – pero no quiero usar SPROCs para hacerlo? En el post de hoy veremos eso – y
veremos cómo podemos usar expresiones SQL personalizadas para que LINQ to SQL las use en lugar de las que
generaría él.
Uso de expresiones LINQ con LINQ to SQL.
Supongamos que hemos usado el diseñador de LINQ to SQL de VS 2008 para modelar un conjunto de clases a partir de
la base de datos Northwind (esto lo vimos en el segundo post de la serie):
En el tercer post vimos cómo podemos usar LINQ con las nuevas características de VB y C# para consultar el modelo
de clases y devolver un conjunto de objetos que representan las filas y columnas de la base de datos.
Por ejemplo, podemos añadir un método a la clase DataContext “GetProductsByCategory” que usa una consulta LINQ
para devolver objetos de Products de la base de datos:
VB:
c#:
Una vez definido nuestro método de LINQ, podemos escribir el siguiente código para obtener productos e iterar sobre
ellos:
VB:
Cuando se evalúa la expresión LINQ del método “GetProductsByCategory”, el ORM LINQ to SQL ejectuará un SQL
dinámico para obtener los datos de la tabla Product para crear los objetos Product. Podeis usar el Visualizador de
Debug de LINQ to SQL para ver en el debugger cuál es la expresión LINQ que se ejectuará.
Uso de consultas SQL personalizadas con LINQ to SQL
En el ejemplo de arriba no tenemos que escribir ningún código SQL para consultar y obtener objetos Product
fuertemente tipados. LINQ to SQL traduce la expresión LINQ a SQL por nosotros.
¿Pero que pasa si queremos un control total sobre el SQL que se está ejecutando en nuestra base de datos, y no
queremos que LINQ to SQL lo haga por nosotros? Una forma de conseguir esto es usando SPROC como ya vimos en las
partes 6 y 7 de esta serie. La otra forma es usar el método auxiliar “ExecuteQuery” de la clase DataContext y usar una
expresión SQL personalizada que le demos.
Usando el método ExecuteQuery
El método ExecuteQuery toma una expresión SQL como argumento , con un conjunto de parámetros, y la ejecuta
contra la base de datos (incluyendo JOINs personalizados sobre varias tablas.
Lo que hace que ExecuteQuery sea tan útil es que nos permite especifiar cómo queremos devolver los valores de la
expresión SQL. Podemos hacer esto pasándole un parámetro tipado al método o usando una versión genérica del
método.
Por ejemplo, podemos cambiar el método GetProductsByCategory() que creamos ántes -con una expresión LINQ- para
que use el método ExecuteQuery para ejectuar un SQL que nosotros le digamos:
VB:
C#:
Ahora podemos llamar al método GetProductsByCategory() de la misma forma que ántes:
De esta manera será nuestra consulta SQL la que se ejecutará contra la base de datos – y no el SQL dinámico que
generaría la expresión LINQ.
SQL personalizado y tracking de objetos para las actualizaciones
Por defecto cuando obtenemos objetos con LINQ to SQL, se hace un tracking sobre los cambios que les hacemos. Si
llamamos al método “SubmitChanges()” guardará los datos de forma transaccional en la base de datos. Vismo esto en
la cuarta parte de esta serie de post.
Una de las característcias del metodo ExecuteQuery() es que participa en este tracking de objetos para actualizar el
modelo. Por ejemplo, podemos escribir el siguiente código para obtener todos los productos de una categoría y rebajar
los precios un 10%:
Como dijimos que el tipo de resultado del ExecuteQuery en el método GetProductsByCategory fuese “Product, LINQ to
SQL sabe cómo guardar los cambios. Y cuando llamemos a SubmitChanges los guardará.
SQL personalizado con clases personalizadas.
El método ExecuteQuery nos permite especificar cualquier clase como tipo de resultado de la consulta SQL. La
clase no tiene porqué haberse creado con el diseñador LINQ to SQL, o implementar ninguna interfaz.
Por ejemplo, definimos la clase ProductSummary con un subconjunto de las propiedades de Product (fijáos que hemos
usado la característica depropiedades automáticas):
Podríamos crear otro método en nuestro DataContext llamado GetProductSummariesByCategory() que nos devuelva
objetos de esa clase. Fijáos cómo la siguiente SQL obtiene sólo un subconjunto de Product – El método ExecuteQuery()
se encarga de mapea automáticamente las propiedades a objetos de la clase ProductSumary:
Ahora podemos invocar a este método e iterar sobre los resultados con el siguiente codigo:
SQL personalizadas para inserciones, actualizaciones y borrados.
Además de usar SQL personalizadas para consultar datos, también podemos hacerlas para insertar, actualizar y borrar
datos.
Esto lo conseguimos creando los métodos parciales adecuados para cada operacion para la entidad que queramos
cambiar en nuestra clase DataContext. Podemos usar el método ExecuteCommand del DataContext para escribir el
SQL que queramos. Por ejemplo, para sobreescribir el comportamiento de borrado de la clase Product definimos el
siguiente método parcial:
Y si escribimos un código que elimine un producto de la base de datos, LINQ to SQL llamará al método DeleteProduct –
que ejecutará una SQL personalizada en lugar del SQL dinámico que LINQ to SQL usaría:
Resumen
El ORM LINQ to SQL genera y ejectua un SQL dinámico para las consultas, actualizaciones, inserciones y borrados
contra la base de datos.
Para escenarios más avanzados, o en caso donde queramos un control total sobre el SQL que se ejecuta, también
podemos personalizar el ORM para que ejecute SPROCs, o nuestras consultas SQL personalizadas. Esto nos da una
gran flexibilidad a la hora de construir y extender nuestra capa de datos.
LINQ to SQL (Parte 9 – Uso de expresiones LINQ personalizadas con el control )
Publicado en .NET, ASP .NET, LINQ, LINQ to SQL, Scott Guthrië, SQL a 12:56 pm por Juanma
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 unprocedimiento 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 laparte 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 laparte 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 unmé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.