Sei sulla pagina 1di 83

20-mayo-2007

Usando LINQ to SQL (1ª Parte)


Publicado en LINQ, LINQ to SQL, Scott Guthrië a 7:18 pm por Juanma

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.

Espero que esta introducción os haya abierto el apetito de aprender más. En las
próximas semanas continuaré esta serie de post explorando el LINQ to SQL en más
detalle.

30-mayo-2007
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.

30-junio-2007
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

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

13-julio-2007
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.

En próximos post veremos el nuevo control <asp:linqdatasource> de la versión 3.5


de .NET, y hablaremos sobre lo fácil que es crear interfaces de usuario en ASP.NET
que se aprovechen de los modelos de datos de LINQ to SQL. También veremos
algunos conceptos de programación más especificos de LINQ to SQL sobre
concurrencia optimista, carga perezosa, herencia de mapeado de tablas, uso de
procedimientos almacenados y sentencias SQL personalizadas, y mucho más.
19-julio-2007
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).
En próximos post veremos en profundidad algunos escenarios con concurrencia
optimista, carga a petición, herencia de mapeado de tablas, y el uso de
procedimientos almacenados y SQL personalizados.

La próxima semana tengo pensado empezar otra serie de post sobre el control
<asp:ListView> – es un nuevo control de ASP.NET en la versión .NET 3.5. Nos
permite un contorl total sobre el código generado para escenarios de datos (sin
tablas, sin spans, ni estilos en linea …), también veremos el soporte para
paginación, ordenado, edición e inserciones. Por ejemplo, podemos usarlo en lugar
del Grid con un look and feel totalmente personalizado. Lo mejor de todo, podemos
cambiar el código de la página anterior sin tener que cambiar nada del modelo de
datos, la declaración del <asp:linqdatasource>, o el code-behind del trato de
errores.

27-abril-2008
Parte 8: Crear la versión de escritorio con WPF
Publicado en Scott Guthrië, Silverlight, Wpf a 2:41 pm por Juanma
Este es el último de los ocho tutoriales en el que estamos creando un cliente de
Digg con la Beta 1 de Silverlight 2. La idea es que estos tutoriales se lean en orden,
con el objetivo de explicar los fundamentos de la programación con Silverlight.

Podéis descargar el código completo del cliente Digg del ejemplo aquí

Crear una aplicación de escritorio con WPF

El objetivo con este último tutorial es un poco diferente del de los otros siete. No
vamos a usar Silverlight en este post – sino que usaremos WPF y .NET 3.5.
Cojeremos el código de la aplicación que estamos creando con Silverlight y lo
reutilizaremos como una aplicación de escritorio.

Silverlight viene con un subconjunto compatible de la API del .NET Framework. Uno
de los objetivos de esto es permitir a los desarrolladores aprender un mismo
modelo de programación que les permita reusar código y contenido rico, tanto para
la web como para aplicaciones escritorio.

Aquí tenéis los pasos que he hecho para convertir la aplicación Silverlight que
hemos creado (que se ejecuta en un navegador) en una aplicación de escritorio
Windows (que no se ejecuta en el navegador).

Paso 1: Crear una nueva aplicación WPF de escritorio

Empezamos creando una nueva aplicación WPF con VS 2008. La llamaremos


“DiggDescktopSample”:
Esto creará un proyecto en VS con dos archivos – un App.xaml y un Window.xaml:

Paso 2: Copiar el código existente en la aplicación WPF

Copiaremos y pegaremos el código que ya tenemos en Silverlight en nuestro nuevo


proyecto:
En la Beta1 esta parte de copiar y pegar es un paso manual – estamos viendo la
posibilidad de tener una forma más automática de hacer esto.

Paso 3: Corregir un par de cositas

He tenido que hacer dos cambios para que nuestro código compile:

1) El esquema de Silverlight Beta1 xmlns: la url es diferente de la versión de WPF.


He tenido que corregir esto en los archivos XAML que hemos copiado en el proyecto
para que apunten al esquema de WPF. Esto es algo que estamos mejorando antes
de publicarlo.

2) He tenido que cambiar el control <WaterMarkTextbox> a un <TextBox> y


cambiar el control <HyperlinkButton> para que sea un <TextBlock>. Estos dos
controles son nuevos en la Beta 1 de Silverlight y no están aún en WPF (los
añadiremos en futuras versiones). No tengo que cambiar ningún código para que
funcione con estos controles, ni siquiera la red, LINQ to XML, ni el código de enlace
a la base de datos.

Una vez que hemos hecho estos cambios, el código se compila sin ningún
problema.

Paso 4: Hosting de la aplicación en una ventana

Entonces abrí el archivo Windows1.xaml en el proyecto de escritorio (que es la


ventana que se carga por defecto cuando arranca la aplicación).
Acutalizé el titulo de la ventana a “Digg Desktop Version” y aumenté el Width y el
Height de la ventana.

Luego añadí nuestro user control Page.xaml del proyecto de Silverlight como el
control raíz de la ventana. Esto hará que sea visible y que se cargue cuando se
cargue la ventana. No tengo que cambiar ningún código de la clase Page ni
renombrar nada. Como hereda de la clase UserControl puede hostearse dentro de
cualquier ventana o control WPF.

Lo último que he tenido que cambiar una cosilla debido a que el servidor de la api
de Digg detecta cuando un cliente no es un navegador y algunas veces le deniega
el acceso (probablemente para evitar scripts automáticos accedan a su servicio).
Esto lo resolví a través de una url de proxy (sin cambiar código, sólo un cambio de
url).

Paso 5: Ejecutar la aplicación

Ya estamos listos para ejecutar la aplicación de Windows. Funciona todo


exactamente igual que con la versión de silverlight:
Y cuando seleccionamos un elemento de la lista, vemos los detalles:
Hay un par de diferencias cosméticas entre la versión del navegador y la de
escritorio. Esto es porque WPF hereda todos los estilos por defecto (fuentes,
colores, scroll bars, etc) basándose en el tema de escritorio del sistema operativo
que está seleccionado, mientras que Silverlight tiene un tema por defecto que
usamos en todos los sistemas operativos. Si queremos que las versiones de
escritorio y la web sean el mismo podemos conseguirlo siendo explícitos en
nuestros estilos y en los templates de los controles – de otra manera la versión de
escritorio se adaptará al tema del usuario.

Resumen

Pronto tendremos algunas notas y sugerencias de buenas prácticas a la hora de


compartir código entre aplicaciones Silverlight y WPF más adelante. Creo que
habéis encontrado las características necesarias para crear aplicaciones Silverlight
y que es muy sencillo pasarlo a proyectos WPF. También estamos trabajando en
que haya una mayor compatibilidad entre este tipo de aplicaciones para que
permitir la reutilización de código entre soluciones, controles, contenido y código
fácilmente.

Potrebbero piacerti anche