linq to sql 4

10

Click here to load reader

Upload: jcfarit

Post on 13-Jun-2015

29 views

Category:

Internet


2 download

DESCRIPTION

Ejemplo de consultas en Linq to Sql Parte4

TRANSCRIPT

Page 1: Linq to sql 4

LINQ to SQL (4ª Parte) – Actualizando la base de datos

43 respuestas

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 2: Linq to sql 4

Cuando definimos el modelo definimos cinco clases: Product, Category, Customer, Order y OrderDetail. Las

propiedades de cada clase mapean las diferentes columnas de las tablas correspondientes en la base de datos. Cada

instancia de cada clase es una entidad que representa una fila de cada tabal.

Cuando definimos nuestro modelo de datos, el diseñador LINQ to SQL creó una clase llamada DataContext que

proporciona todo lo necesario para poder consultar/actualizar la base de datos. En nuestro ejemplo, esta clase se llama

NorthwindDataContext. Ésta clase tiene unas propiedades que representan cada tabla modelada de la base de datos (en

concreto: Products, Categories, Customers, Orders y OrderDetails).

Como vimos en el tercer post de esta serie, podemos usar expresiones LINQ para consultar y obtener datos usando la

clase NorthwindDataContext.LINQ to SQL traduce automáticamente estas expresiones LINQ al código SQL

apropiado en tiempo de ejecución.

Por ejemplo, la siguiente expresión devuelve un objeto Product buscando el nombre del producto:

La siguiente consulta nos devuelve todos los productos de la base de datos que no han sido pedidos, y cuyo precio es

mayor de 100 dólares:

Estamos usando la asociación "OrderDetails" de cada producto como parte de la consulta sólo para obtener aquellos

productos que no se han pedido.

Seguir los cambios y DataContext.SubmitChanges()

Cuando creamos consultas y obtenemos objetos como en los ejemplos anteriores, LINQ to SQL estará pendiente de los

cambios o actualizaciones que les hagamos a los objetos. Podemos hacer tantas consultas y cambios como queramos

usando la clase DataContext de LINQ to SQL, sabiendo que dichos cambios serán supervisados a la vez:

Nota: El seguimiento de cambios de LINQ to SQL ocurre en el lado del consumidor - y NO en la base de datos. Es

decir, no estamos consumiendo ningún recurso de la base de datos mientras lo usemos, tampoco tenemos que

cambiar/instalar nada en la base de datos para que esto funcione.

Después de realizar los cambios que queramos a los objetos que hemos obtenido con LINQ to SQL, podemos llamar al

método "SubmitChanges()" de nuestro DataContext para guardar los cambios en nuestra base de datos. Con esto,

LINQ to SQL, creara y ejecutará las sentencias SQL apropiadas para actualizar la base de datos.

Por ejemplo, el siguiente código actualiza el precio y las unidades en stock del producto "Chai" en la base de datos:

Page 3: Linq to sql 4

Cuando llamamos al método northwind.SubmitChanges(), LINQ to SQL creará y ejecutará las sentencias "UPDATE"

de SQL necesarias para guardar las propiedades modificadas.

Con el siguiente código iteramos sobre los productos menos populares y caros y ponemos la propiedad "ReorderLevel"

a cero.

Cuando llamamos al método northwind.SubmitChanges(), LINQ to SQL crea y ejecuta las sentencias UPDATE de

SQL necesarias para modificar los productos a los que hemos modificado la propiedad ReorderLevel.

Vemos que si no se ha modificado alguna propiedad de un Product con la asignación anterior, LINQ to SQL no

ejecutará ninguna actualización para ese objeto. Por ejemplo - si el precio del producto "Chai" era 2 dolares, y el

número de unidades en stock era cuatro, la llamada a SubmitChanges() no actualizara esos valores. Sólo los productos

cuyo ReorderLevel no era 0 se actualizarán.

Ejemplos de inserción y borrado

Además de poder actualizar la base de datos, LINQ to SQL también nos permite insertar y eliminar datos. Esto lo

conseguimos añadiendo o eliminando objectos de las colecciones disponibles en DataContest, y luego llamar al método

SubmitChanges(). LINQ to SQL "monitorizará" esas inserciones y borrados, y generará el código SQL necesario

cuando se invoque a SubmitChanges()

Añadiendo un producto

Podemos añadir un producto a la base de datos creando un nuevo objeto "Product", inicializando sus propiedades y

añadirlo a la colección "Products" de nuestro DataContext:

Cuando llamemos a SubmitChanges() se añadirá una nueva fila en la tabla de productos.

Borrando productos

Page 4: Linq to sql 4

De la misma forma que añadimos un nuevo producto a la base de datos añadiendo un objeto Product a la colección

Products del DataContext, también podemos borrar productos borrándolos de esa misma colección:

Lo que estamos haciendo es obtener una secuencia de productos "alternados" de la tabla, es decir, no ordenados por

ninguna expresión LINQ, y luego esa secuencia se la pasamos al método RemoveAll() de la colección "Products".

Cuando llamamos a SubmitChanges() todos esos productos serán borrados de la tabla

Actualizaciones y relaciones

Lo que hace que los O/RM's como LINQ to SQL sean tan flexibles es que también nos permiten modelar las relaciones

entre las tablas. Por ejemplo, podemos modelar que cada producto tenga una categoría, que cada pedido tenga un

detalle de pedido, asociar cada detalle de pedido con un producto, y tener un conjunto de pedidos en cada cliente. Ya

vimos cómo modelar las relaciones en la segunda parte de esta serie de post.

LINQ to SQL nos permite aprovechar estas relaciones tanto para consultar como para actualizar nuestros datos. Por

ejemplo, con el siguiente código creamos un nuevo producto y lo asociamos con la categoría "Beverages":

Estamos añadiendo el objeto producto en la colección de categorías de productos. Esto indicará que hay una relación

entre dos objetos, y hará que LINQ to SQL mantenga automáticamente las relaciones de clave primaria/ajena entre los

dos cuando llamemos a SubmitChanges().

Veamos otro ejemplo para ver cómo LINQ to SQL nos ayuda a mantener limpio el código referente a las relaciones

entre las tablas. En el siguiente ejemplo estamos creando un nuevo pedido para un cliente existente. Después de

rellenar las propiedades necesarias, podemos crear dos objetos de detalles de pedido y asociarlo a un pedido de un

cliente y actualizaremos la base de datos con todos los cambios:

Page 5: Linq to sql 4

Como vemos, el modelo de programación que hemos usado para hacer todo esto es realmente limpio y orientado a

objetos.

Transacciones

Una transacción es un servicio de la base de datos que garantiza que un conjunto de acciones individuales van a

suceder de forma atómica - es decir, o se pueden completar todas o si hay alguna que falle, todas las demas se

descartarán, y el estado de la base de datos será el mismo que ántes de comenzar la transacción.

Cuando llamemos a SubmitChanges(), las actualizaciones se mapean en una única transacción. Es decir, la base de

datos no tendrá nunca un estado inconsistente si hacemos muchos cambios - tanto si se hacen las actualizaciones como

si no.

Si no hay ninguna transacción en curso, el objeto DataContext empezará una transacción de la base de datos para

guardar las actualizaciones que hagamos con SubmitChanges(). Pero LINQ to SQL también nos permite definir

explícitamente y usar nuestro propio sistema de transacciones (introducido en la versión 2.0 de .NET). Esto hace más

fácil aún integrar código LINQ to SQL con el código de acceso a datos que ya tengamos. También nos permite encolar

recursos que no son propios de la base de datos en la misma transacción - por ejemplo: podemos enviar un mensage

MSMQ, actualizar el sistema de archivos (usando el nuevo soporte transaccional de sistemas de archivos), etc - y

enlazar todas estas tareas en una sola transacción a la hora de actualizar la base de datos

Validación y lógica de negocio

Una de las cosas más importantes que los desarrolladores tienen que hacer cuando trabajan con datos es incorporar

validación y reglas de negocio. LINQ to SQL tiene varias formas para hacer que los desarrolladores puedan hacer eso

de forma fácil y clara.

LINQ to SQL nos permite añadir esta validación lógica una vez. De forma que no tendremos que repetir esa lógica en

varios sitios, con lo que conseguimos un modelo de datos más mantenible y más claro.

Soporte de validación de esquemas

Cuando definimos el modelo de clases de datos con el diseñador de LINQ to SQL de VS 2008, se añadirán algunas

reglas de validación obtenidas del esquema de las tablas de la base de datos.

Page 6: Linq to sql 4

Los tipos de datos de las propiedades de las clases del modelo de datos coincidirán con el esquema de la base de datos.

Con esto tendremos errores de compilación si intentamos asignar un booleano a un valor decimal, o si convertirmos

tipos numéricos incorrectamente.

Si una columna en la base de datos está marcada como nullable, la propiedad correspondiente que crea el diseñador de

LINQ to SQL será un tipo nullable. Las columnas marcadas como no nullables lanzarán excepciones si no les

asignamos ningun valor. LINQ to SQL también se asegurará que de que los valores identidad/unicos se asignan

correctamente.

Obviamente podemos usar el diseñador LINQ to SQL para sobreescribir los valores por defecto del esquema si

queremos - pero por defecto, las tendremos automáticamente sin tener que hacer nada. LINQ to SQL también

comprueba los valores de los parámetros de las consultas SQL, de manera que no tendremos que preocuparnos por los

ataques de inyección de SQL.

Soporte para validación personalizada de propiedades

La validación de datos a través de esquemas es muy útil, pero no suele ser suficiente en escenarios reales.

Imaginemos que en la base de datos Northwind tenemos una propiedad "Phone" en la clase "Customer" que está

definida en la base de datos como nvarchar. Usando LINQ to SQL podemos escribir el siguiente código para

actualizarlo con un número de teléfono válido:

El problema que nos encontraríamos, sería que el siguiente código sigue siendo válido desde el punto de vista de un

esquema SQL (ya que sigue siendo una cadena, no un número de teléfono válido).

Para no permitir que no se puedan meter números de teléfono erróneos en nuestra base de datos, podemos añadir una

regla de validación personalizada a la clase Customer de nuestro modelo de datos. Es realmente fácil, todo lo que

necesitamos hacer es añadir una nueva clase parcial a nuestro proyecto que defina el siguiente método:

Este código usa dos caracteristicas de LINQ to SQL:

Page 7: Linq to sql 4

1. Todas las clases que genera el diseñador LINQ to SQL son "parciales" - es decir, podemos añadir métodos

adicionales, propiedades, y eventos (en archivos separados). Así podemos extender nuestro modelo de clases

creada por el diseñador de LINQ to SQL con reglas de validación y métodos auxiliares que definamos. No es

necesario ninguna configuración.

2. LINQ to SQL expone una serie de puntos de extensión en el modelo de datos que podemos usar para añadir

validación lógica. Muchos de estos puntos de extensión usan la nueva característica llamada "métodos

parciales" que viene con VB y C# en VS 2008 Beta2. Wes Dyer el equipo de C# ha escrito un post explicando

cómo va esto de los métodos parciales.

En nuestro ejemplo de validación, estamos usando el método parcial OnPhoneChangin que se ejecuta cada vez que se

cambia el valor de la propiedad "Phone" de un objeto "Customer". Podemos usar este método para validar la entrada de

datos (en este caso estamos usan una expresión regular). Si todo va bien, LINQ to SQL asumirá que el valor es válido.

Si hay algún problema con el valor, podemos lanzar una excepción en el método de validación - que hará que la

asignación no se haga.

Soporte para validación personalizada de objetos entidad.

En el punto anterior hemos visto cómo añadir validación a una propiedad individual de nuestro modelo de datos. Sin

embargo, algunas veces, necesitamos/queremos validar validar multiples propiedades de un objeto.

Veamos un ejemplo, tenemos un objeto Order y queremos poner las propiedades "OrderDate" y "RequiredDate":

Este código es legal desde el punto de vista de SQL - aunque no tenga ningún sentido la propiedad de fecha de entrega,

que era para ayer.

LINQ to SQL en Beta2 nos permite añadir reglas de validación a nivel de entidad para corregir este tipo de errores.

Podemos añadir una clase parcial para nuestra entidad "Order" e implementar el método parcial OnValidate() que se

invocará ántes de que se guarden los datos en la base de datos. De esta forma, podemos acceder y validar todas las

propiedades de nuestro modelo de datos:

De esta forma podemos validar cualquiera de las propiedades de la entidad (incluso obtener acceso de sólo lectura a los

objetos asociados), y lanzar una excepción si el valor es incorrecto. Cualquier excepción lanzada desde el método

OnValidate() abortará cualquier cambio que queramos hacer en la base de datos, y deshacer todos los cambios hechos

en la transacción actual.

Validación en los métodos de inserción/actualización/borrado.

A menudo necesitamos añadir validación específica en los métodos de inserción, actualización o borrado. LINQ to

SQL nos lo permite añadiendo una clase parcial que extienda a la clase DataContext e implementar métodos parciales

Page 8: Linq to sql 4

para personalizar la lógica de inserción, actualización y borrado de las entidades de nuestro modelo de datos. Estos

métodos serán llamados automáticamente cuando invoquemos a SubmitChanges().

Podemos añadir la validación lógica que estimemos oportuna con estos métodos - y si todo va bien, LINQ to SQL

continará guardando los datos en la base de datos (llamando al método de DataContext "ExecuteDynamicXYZ").

Podemos añadir métodos que se invocarán automáticamente cuando se vayan a crear/actualizar/borrar datos. Por

ejemplo, supongamos que queremos crear un nuevo pedido y asociarlo con un cliente existente:

Cuando llamamos a northwind.SubmitChanges(), LINQ to SQL determinará que es necesario guardar el nuevo objeto

Order, y ejecutará nuestro método parcial "InsertOrder".

Avanzado: Viendo la lista de cambios de la transacción

Hay veces que no nos interesa añadir validación lógica a elementos individuales, sino que queremos ser capaces de ver

toda la lista de cambios que están ocurriendo en una transacción.

Page 9: Linq to sql 4

Desde la Beta2 de .NET 3.5, LINQ to SQL nos permite acceder a la lista de cambios a través del método

DataContext.GetChangeList(). Nos devolverá un objeto ChangeList que expone una serie de colecciones de adiciones,

borrados y modificaciones que se han hecho.

Una aproximación que podemos hacer en algunos escenarios es crear una clase parcial de la clase DataContext y

sobreescribir su método SubmitChange(). Podemos obtener la lista de ChangeList() para las operaciones de

actualizaciones y crear cualquier validación que queramos:

Este ejemplo es un caso de uso avanzado - pero es interesante saber que siempre podremos extender y aprovecharnos

de esta forma de las nuevas características de LINQ to SQL.

Administrando cambios simultáneos con concurrencia optimista.

Una de las cosas en las que tenemos que pensar los desarrolladores en entornos multi-usuarios es cómo administrar las

actualizaciones de los mismos datos en la base de datos. Por ejemplo, imaginemos que tenemos dos usuarios que

obtienen un objeto product, y uno de ellos cambia el ReorderLevel a 0 mientras que el otro lo pone a 1. Si ambos

usuarios guardan esos cambios en la base de datos, el desarrollador tiene que decidir cómo tratar ese conflicto.

Una solución es dejar que sea el último que lo guarda - es decir, que el valor que el primer usuario guardó se perderá

sin que éste se de cuenta. Esta es una solución muy pobre (e incorrecta).

Otra solución que permite LINQ to SQL es usar el modelo de concurrencia optimista, es decir, LINQ to SQL detectará

automáticamente si el valor original de la base de datos ha sido actualizado por alguien ántes que se guarden los

nuevos datos. LINQ to SQL nos da una lista de conflictos de valores cambiados al desarrollador y nos permite tanto

hacer lo que queramos como avisar al usuario de la aplicación para que nos indique el propio usuario lo que quiere

hacer.

Ya veremos en más detalle este tema en un próximo post.

Uso de procedimientos almacenados o lógica SQL personalizada para insertar, actualizar y borrar.

Una de las preguntas que tienen los desarrolladores (en especial los DBAs), que suelen escribir procedimientos

almacenados con SQL personalizadas, cuando ven LINQ to SQL por primeravez es: "¿pero cómo podemos tener

control absoluto del SQL que se está ejecutando?".

Las buenas noticias son que LINQ to SQL tiene un modelo muy flexible que nos permite sobreescribir el SQL que se

está ejecutando, y llamar a los procedimientos almacenados que desarrollemos para añadir, actualizar o borrar datos.

Lo realmente increible es que podemos empezar definiendo nuestro modelo de datos y dejar que LINQ to SQL

administre las inserciones, actualizaciones y borrados. Una vez hecho esto, podemos personalizar el modelo de datos

para que use nuestros propios procedimientos almacenados o nuestras sentencias SQL - sin tener que cambiar nada de

la lógica de aplicación que estamos usando para nuestro modelo de datos, ni cambiar nada de las validaciones ni de la

lógica de negocio. Esto nos da una gran flexibilidad a la hora de construir nuestra aplicación.

Page 10: Linq to sql 4

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.