123
Usando LINQ to SQL (Introducción) Gracias a la traduccion de VIO podemos disfrutar de las bondades que LINQ que viene incluido en el Nuevo Visual Studio puede brindarle al desarrollador… Hay que recorda que segun Wikipedia Language Integrated Query (LINQ) es un proyecto de Microsoft que agrega consultas nativas semejantes a las de SQL a los lenguajes del .NET Framework, inicialmente a los lenguajes Visual Basic .NET y C#. Muchos conceptos que LINQ ha introducido fueron originalmente probados en Cω, un proyecto de investigación de Microsoft. LINQ define operadores de consulta estándar que permiten a lenguajes habilitados con LINQ filtrar, enumerar y crear proyecciones de varios tipos de colecciones usando la misma sintaxis. Tales colecciones pueden incluir arreglos, clases enumerables, XML, conjuntos de datos desde bases de datos relacionales y origenes de datos de terceros. El proyecto LINQ usa características de la versión 2.0 del .NET Framework, nuevos ensamblados relacionados con LINQ, y extensiones para los lenguajes C# y Visual Basic .NET. Microsoft ha distribuido una versión previa del LINQ, consistente de estas bibliotecas y compiladores para C# 3.0 y Visual Basic 9. Otros lenguajes, como F# y Nemerle, han anunciado brindar soporte preliminar.

Usando LINQ to SQL

Embed Size (px)

DESCRIPTION

Manual que enseña a utilizar LINQ to SQL como modelo ORM, el uso de los ORM está haciendo un gran avance en el area de desarrollo de software

Citation preview

Page 1: Usando LINQ to SQL

Usando LINQ to SQL (Introducción)

Gracias a la traduccion de VIO podemos disfrutar de las bondades que LINQ que viene

incluido en el Nuevo Visual Studio puede brindarle al desarrollador…

Hay que recorda que segun Wikipedia Language Integrated Query (LINQ) es un

proyecto de Microsoft que agrega consultas nativas semejantes a las de SQL a los lenguajes

del .NET Framework, inicialmente a los lenguajes Visual Basic .NET y C#. Muchos

conceptos que LINQ ha introducido fueron originalmente probados en Cω, un

proyecto de investigación de Microsoft.

LINQ define operadores de consulta estándar que permiten a lenguajes habilitados con

LINQ filtrar, enumerar y crear proyecciones de varios tipos de colecciones usando la

misma sintaxis. Tales colecciones pueden incluir arreglos, clases enumerables, XML,

conjuntos de datos desde bases de datos relacionales y origenes de datos de terceros. El

proyecto LINQ usa características de la versión 2.0 del .NET Framework, nuevos

ensamblados relacionados con LINQ, y extensiones para los lenguajes C# y Visual Basic

.NET. Microsoft ha distribuido una versión previa del LINQ, consistente de estas

bibliotecas y compiladores para C# 3.0 y Visual Basic 9. Otros lenguajes, como F# y

Nemerle, han anunciado brindar soporte preliminar.

Page 2: Usando LINQ to SQL

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‖:

Page 3: Usando LINQ to SQL

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

Page 4: Usando LINQ to SQL

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:

Page 5: Usando LINQ to SQL

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#:

Page 6: Usando LINQ to SQL

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#:

Page 7: Usando LINQ to SQL

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.

Page 8: Usando LINQ to SQL

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.

Page 9: Usando LINQ to SQL

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.

Page 10: Usando LINQ to SQL

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:

Page 11: Usando LINQ to SQL

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.

Page 12: Usando LINQ to SQL

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

Page 13: Usando LINQ to SQL

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:

Page 14: Usando LINQ to SQL

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:

Page 15: Usando LINQ to SQL

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):

Page 16: Usando LINQ to SQL

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:

Page 17: Usando LINQ to SQL

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

Page 18: Usando LINQ to SQL

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:

Page 19: Usando LINQ to SQL

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

Page 20: Usando LINQ to SQL

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.

Page 21: Usando 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:

Page 22: Usando LINQ to SQL

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:

Page 23: Usando LINQ to SQL

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:

Page 24: Usando LINQ to SQL

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.

Page 25: Usando LINQ to SQL

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:

Page 26: Usando LINQ to SQL

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:

Page 27: Usando LINQ to SQL

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):

Page 28: Usando LINQ to SQL

Lo realmente útil de LINQ to SQL es que podemos aprovecharnos de las asociaciones entre

clases de nuestro modelo de datos cuando restringimos los datos. Esto nos permite expresar

consultas útiles y muy eficientes. Por ejemplo, la siguiente consulta obtiene los ID y los

nombres de la entidad Product, el número total de pedidos que hemos hecho de productos,

y los suma al total de pedidos de Productos:

La expresión a la derecha de la propiedad ―Revenue‖ es un ejemplo del uso del método de

extensión ―Sum‖ de LINQ. Toma una expresión Lambda que devuelve el valor de cada

pedido de producto como argumento.

LINQ to SQL es listo y es capaz de transformar la expresión LINQ anterior al siguiente

SQL cuando es evaluado (con el visor del debugger):

Page 29: Usando LINQ to SQL

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):

Page 30: Usando LINQ to SQL

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:

Page 31: Usando LINQ to SQL

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:

Page 32: Usando LINQ to SQL

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:

Page 33: Usando LINQ to SQL

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:

Page 34: Usando LINQ to SQL

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.

Page 35: Usando LINQ to SQL

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:

Page 36: Usando LINQ to SQL

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:

Page 37: Usando LINQ to SQL

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.

Page 38: Usando LINQ to SQL

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:

Page 39: Usando LINQ to SQL

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‖:

Page 40: Usando LINQ to SQL

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.

Page 41: Usando LINQ to SQL

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.

Page 42: Usando LINQ to SQL

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.

Page 43: Usando LINQ to SQL

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:

Page 44: Usando LINQ to SQL

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‖).

Page 45: Usando LINQ to SQL

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‖.

Page 46: Usando LINQ to SQL

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).

Page 47: Usando LINQ to SQL

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.

Page 48: Usando LINQ to SQL

LINQ to SQL (5ª Parte) – Enlazar controles de interfaz

de usuario con el ASP:LinqDatSource

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.

Page 49: Usando 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.

Page 50: Usando LINQ to SQL

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 el patró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:

Page 51: Usando LINQ to SQL

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‖:

Page 52: Usando LINQ to SQL

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:

Page 53: Usando LINQ to SQL

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:

Page 54: Usando LINQ to SQL
Page 55: Usando LINQ to SQL

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):

Page 56: Usando LINQ to SQL

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.

Page 57: Usando LINQ to SQL

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:

Page 58: Usando LINQ to SQL

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:

Page 59: Usando LINQ to SQL

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.

Page 60: Usando LINQ to SQL

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.

Page 61: Usando LINQ to SQL

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:

Page 62: Usando LINQ to SQL

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:

Page 63: Usando LINQ to SQL

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:

Page 64: Usando LINQ to SQL

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

Page 65: Usando LINQ to SQL

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:

Page 66: Usando LINQ to SQL

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:

Page 67: Usando LINQ to SQL

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).

Page 68: Usando LINQ to SQL

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.

Page 69: Usando LINQ to SQL

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:

Page 70: Usando LINQ to SQL

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:

Page 71: Usando LINQ to SQL

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:

Page 72: Usando LINQ to SQL

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.

Page 73: Usando LINQ to SQL

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.

Page 74: Usando LINQ to SQL

LINQ to SQL (Parte 6 – Obtener datos con

procedimientos almacenados)

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 puede encapsular 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.

Page 75: Usando LINQ to SQL

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.

Page 76: Usando LINQ to SQL

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.

Page 77: Usando LINQ to SQL

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:

Page 78: Usando LINQ to SQL

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:

Page 79: Usando LINQ to SQL

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:

Page 80: Usando LINQ to SQL

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>

Page 81: Usando LINQ to SQL

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).

Page 82: Usando LINQ to SQL

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‖:

Page 83: Usando LINQ to SQL

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.

Page 84: Usando LINQ to SQL

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#:

Page 85: Usando LINQ to SQL

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:

Page 86: Usando LINQ to SQL

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‖:

Page 87: Usando LINQ to SQL

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:

Page 88: Usando LINQ to SQL

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.

Page 89: Usando LINQ to SQL

LINQ to SLQ (Parte 7 – Actualizando la base de datos

con procedimientos almacenados)

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:

Page 90: Usando LINQ to SQL

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).

Page 91: Usando LINQ to SQL

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:

Page 92: Usando LINQ to SQL

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

Page 93: Usando LINQ to SQL

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.

Page 94: Usando LINQ to SQL

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:

Page 95: Usando LINQ to SQL

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

Page 96: Usando LINQ to SQL

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.

Page 97: Usando LINQ to SQL

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‖:

Page 98: Usando LINQ to SQL

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).

Page 99: Usando LINQ to SQL

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:

Page 100: Usando LINQ to SQL

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:

Page 101: Usando LINQ to SQL

Hacemos clic en el botón ―…‖ y aparecerá una ventana que nos permite personalizar las

operaciones de inserción:

Page 102: Usando LINQ to SQL

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.

Page 103: Usando LINQ to SQL

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.

Page 104: Usando LINQ to SQL

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:

Page 105: Usando LINQ to SQL

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.

Page 106: Usando LINQ to SQL

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

Page 107: Usando 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

Page 108: Usando LINQ to SQL

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.

En futuros post veremos más conceptos sobre LINQ to SQL como: Herencia simple de

tablas, Carga retrasada, concurrencia optimista, y administración de escenarios de N-capas.

ESta semana estaré de vacaciones y espero tener más tiempo libre para escribir alguno de

ellos.

Page 109: Usando LINQ to SQL

LINQ to SQL (Parte 8 – Ejecutar consultas

SQL personalizadas)

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.

Page 110: Usando LINQ to SQL

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.

Page 111: Usando LINQ to SQL

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#:

Page 112: Usando LINQ to SQL

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.

Page 113: Usando LINQ to SQL

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:

Page 114: Usando LINQ to SQL

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 de propiedades 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

Page 115: Usando LINQ to SQL

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:

Page 116: Usando LINQ to SQL

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.

En próximos post veremos algunos conceptos de LINQ to SQL como: Herenacia simple de

talbas, carga a petición, concurrencia optimista, y escenarios de N-capas.

Page 117: Usando LINQ to SQL

LINQ to SQL (Parte 9 – Uso de expresiones LINQ

personalizadas con el control )

En las últimas semanas he escrito una serie de post sobre LINQ to SQL. LINQ to SQL es

un ORM que viene con .NET 3.5, y nos permite modelar bases de datos relacionales en

clases. Podemos usar expresiones LINQ para consultar la base de datos y también para

actualizar, insertar y borrar datos.

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

Parte 1: Introducción a LINQ to SQL

Parte 2: Definiendo el modelo de datos.

Parte 3: Consultando la base de datos

Parte 4: Actualizando la base de datos.

Parte 5: Enlazar controles de interfaz de usuario con el ASP:LinqDatSource

Parte 6: Obtener datos con procedimientos almacenados.

Parte 7: Actualizando la base de datos con procedimientos almacenados.

Parte 8: Ejecutar consultas SQL personalizadas.

En la quinta parte vimos el control <asp:LinqDataSource> de .NET 3.5 y hablamos sbre

cómo podemos enlazar controles de ASP.NET a LINQ to SQL. También vimos cómo

usarlo con el control <asp:ListView> (El control asp:ListView (Parte 1 – Creación de una

página de listado de productos con una CSS limpia))

En ambos artículos las consultas que hacíamos eran relativamente sencillas (la clausula

where se ejecutaba sobre una tabla simple). En el post de hoy veremos cómo usar toda la

potecia de las consultas de LINQ con el control LinqDataSource, y veremos cómo usar

cualquier expresion LINQ to SQL con él.

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

En estos dos post vimos cómo usar el filtro del control LinqDatasource para declarar un

filtro en un modelo LINQ to SQL.

Por ejemplo, supongamos que hemos creado un modelo LINQ to SQL de la base de datos

Northwind (que ya vimos en la segunda parte de esta serie), podríamos declarar un control

<asp:LinqDataSource> en la página con un filtro <where> que devuelve aquellos productos

de una categoría dada. (especificada a partir del valor ―categoryid‖).

Page 118: Usando LINQ to SQL

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?

Page 119: Usando LINQ to SQL

Uso del evento Selecting del <asp:LinqDataSource>

En consultas personalizadas podemos implementar un manejador de eventos para el evento

―Selecting‖ en el control <asp:LinqDataSource>. Con este manejador podemos escribir el

código que queramos para obtener los datos. Esto lo podemos hacer con una expresión

LINQ to SQL, o llamar a un procedimiento almacenado o usar una expresión SQL

personalizada. Una vez que obtenemos la secuencia de datos, todo lo que tenemos que

hacer es asignar la propiedad ―Result‖ al objeto LinqDataSourceSelectEventArgs. El

<asp:LinqDataSource> usará esta secuencia como los datos con los que trabajará.

Por ejemplo, aquí tenéis una consulta LINQ to SQL que obtiene aquellos productos de los

proveedores de un conjunto de países:

VB:

C#:

Nota: No tenemos que escribir la consulta en el código del manejador. Una solución más

limpia sería encapsularla en un método de ayuda al que podríamos llamar desde el propio

manejador. Esto lo vimos en la parte 8 de esta serie (usando el método

GetProductsByCategory).

Page 120: Usando LINQ to SQL

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.

Page 121: Usando LINQ to SQL

En nuestro evento ―Selecting‖ estamos declarando una consulta LINQ personalizada que

queremos ejecutar y luego se la asignamos a la propiedad ―e.Result‖. Pero aún no la hemos

ejecutado (ya que no hemos iterado sobre los resultados o llamado a los métodos ToArray()

o ToList()). El LINQDataSource es capaz de añadir automáticamente los operadores Skip()

y Take() al aconsulta, así como aplicarle una expresión ―orderby‖ — siendo todos estos

valores calculados automáticamente a partir del índice de página y las preferencias de

ordenación del GridView. Sólo entonces el LINQDataSource ejecuta la expresión LINQ y

obtiene los datos. LINQ to SQL se encarga de que la lógica de ordenación y paginado se

haga en la base de datos – y que sólo se devuelvan 10 filas de productos.

Fijáos cómo podemos seguir usando el GridView para editar y borrar datos, incluso cuando

usamos el evento ―Selecting‖ del LINQDataSource:

El soporte de edicion/borrado funcionará mientras que el evento Selecting asigne la

secuencia de resultados a la propiedad Result y sean objetos entidad (por ejemplo: una

secuencia de Product, Supplier, Category, Order, etc). El LinqDataSource administrará los

casos en el que los controles hagan actualizaciones sobre ellos.

Para leer mas sobre cómo funcionan las actualizaciones con LINQ to SQL, leed la parte

cuatro de esta serie, y luego la parte quinta para ver las Updates en accción.

Page 122: Usando LINQ to SQL

Realizano proyecciones de consultas personalizadas con el evento Selecting.

Una de las características más poderosas de LINQ es la habilidad de ―formar‖ o ―proyectar‖

datos. Podemos hacer esto en una expresión LINQ to SQL para indicar que queremos

obtener sólo un subconjunto de valores de una entidad, y/o calcular nuevos valores

dinámicamente al vuelo con expresiones personalizadas que definamos. Para leer más sobre

esto leed la tercera parte de la serie.

Por ejemplo, podemos modificar el evento ―Selecting‖ para calcular un GridView para que

muestre un subconjunto de información de Product. En el grid queremo mostrar el

ProductID, ProductName, Product UnitPrice, el número de pedidos de ese producto, y el

total de pedidos de ese producto. Podemos calcular estos dos últimos campos con la

siguiente expresión LINQ:

VB:

C#:

Nota: El método Sum para calcular el Revenue es un ejemplo de un método de extensión.

La función es una expresión lambda. El tipo de resultados creados de la consulta LINQ es

un tipo anónimo - ya que el tipo es inferido de la consulta. Métodos de extensión,

expresiones Lambda, y los tipos anónimos son nuevas características de VB y C# en VS

2008.

Page 123: Usando LINQ to SQL

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.