Download pdf - Linq to sql 7

Transcript
Page 1: Linq to sql 7

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

con procedimientos almacenados)

9 respuestas

En las últimas semanas he escrito una serie de post sobre LINQ to SQL. Es un ORM integrado en .NET 3.5, y nos

permite modelar bases de datos relacionales con clases de .NET. Podemos usar expresiones LINQ para consultar a la

base de datos, actualiazarla, insertar y borrar datos.

Aquí tenéis los enlaces a los otros post:

Parte 1: Introducción a LINQ to SQL

Parte 2: Definiendo el modelo de datos.

Parte 3: Consultando la base de datos

Parte 4: Actualizando la base de datos.

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

Parte 6: Obtener datos con procedimientos almacenados.

En la sexta parte vimos cómo podemos usar procedimientos almacenados (SPROCs) y funciones definidas por el

usuario (UDFs) para consultar la base de datos con el modelo de datos de LINQ to SQL. En el post de hoy veremos

cómo podemos usar los SPROCs para actualizar/insertar/borrar datos de nuestra base de datos.

Para ayudar a entender esto empezaremos costruyendo una capa de datos para la base de datos de ejemplo Northwind:

Paso 1: Crear nuestra capa de acceso a datos (sin SPROCs)

En la segunda parte de esta serie vimos cómo usar el diseñador de LINQ to SQL de VS 2008 para crear el siguiente

modelo de clases:

Page 2: Linq to sql 7

Añadiendo reglas de validación a nuestro modelo de clases.

Después de definir nuestro modelo querremos añadir reglas de validación a nuestro modelo de datos. Podemos hacer

esto añadiendo clases parciales a nuestro proyecto y añadir las reglas de validación en esas clases (vimos cómo hacer

esto en la cuarta parte de esta serie).

Por ejemplo, podemos añadir la lógica necesaria para asegurarnos de que el número de teléfono de los clientes siguen

un patrón válido, y otra para asegurarnos de que la fecha de entrega (RequierdDate) es posterior a la fecha actual del

pedido (OrderDate). Una vez que hemos definido las clases parciales, estos métodos de validación se ejecutarán cada

vez que escribamos código para actualizar nuestros objetos de datos de nuestra aplicación:

VB:

Page 3: Linq to sql 7

C#:

Page 4: Linq to sql 7

Añadir un método de ayuda GetCustomer() a nuestro DataContext

Una vez que hemos creado nuestro modelo de clases, y que le hemos añadido reglas de validación, podemos consultar

e interactuar con los datos. Podemos hacer esto escribiendo expresiones LINQ sobre nuestro modelo de clases (vimos

cómo hacer esto en la tercera parte de esta serie). También podemos mapear SPROCs en nuestro DataContext (esto lo

vimos en la sexta parte de la serie).

Cuando creamos una capa de datos con LINQ to SQL normalmente querremos encapsular consultas comunes de LINQ

(o SPROCs) en métodos auxiliares que añadiremos a la clase DataContext. Esto lo conseguimos añadiendo una clase

parcial a nuestro proyecto. Por ejemplo, podemos añadir un método llamado "GetCustomer()" que nos permita buscar

y obtener objetos Customer de la base de datos a partir del valor CustomerID:

VB:

Page 5: Linq to sql 7

C#:

Paso 2: Usando nuestra capa de datos (seguimos sin SPROCs)

Ya tenemos una capa de datos que encapsula nuestro modelo de datos, integra reglas de validación, y nos permite

consultar, actualizar, insertar y borrar datos.

Veamos ahora un escenario simple usándolo para obtener un objeto customer existente, actualizamos el ContactName

y el PhoneNumber, y creamos un nuevo objeto Order para asociarlos. El siguiente código hace todo eso en una sola

transacción. LINQ to SQL se asegura de que las reglas de validación se cumplen ántes de guardar nada en la base de

datos:

VB:

Page 6: Linq to sql 7

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.

Page 7: Linq to sql 7

Esta simetría en el modelo de programación es muy potente ya que no tenemos que aprender dos maneras diferentes de

hacer las cosas, ni tenemos que decidir al principio del proyecto qué técnica usar, si SPROC o no. Podemos empezar

usando el SQL dinámico que nos da LINQ to SQL para las consultas, inserciones, actualizaciones y borrados. Podemos

añadir reglas de validación a nuestro modelo. Y luego podemos actualizar el modelo de datos para usar SPROCs - o

no. El código y los test que escribamos contra las clases del modelo de datos serán exáctamente iguales.

De ahora en adelante veremos cómo podemos actualizar nuestro modelo de datos usando SPROCs para

actualizar/insertar/borrar - mientras seguimos usando las mismas reglas de validación y trabajaremos con los mismos

códigos anteriores.

Cómo usar SPROCs en inserciones, actualizaciones y borrados

Podemos modificar la capa de datos que estamos construyendo para que use SPROCs, en lugar de SQL dinámico de

dos maneras:

1. Usando el diseñador de LINQ to SQL para configurar gráficamente la ejecución de los SPROCs en las

diferentes operaciones o

2. Añadir una clase parcial NorthwindDataContext a nuestro proyecto, y entonces implementar los métodos

necesarios para la inserción, borrado y actualización. (por ejemplo: InsertOrder, UpdateOrder, DeleteOrder)

que serán llamados cuando se realize alguna de las operaciones asociadas. Estos métodos parciales serán

pasados a las instancias del modelo de datos que queramos actualizar, y podemos ejecutar tanto SPROC como

código SQL para guardarlo.

Cuando usemos la primera aproximación para configurar gráficamente los SPROCs que llamaremos, por debajo se está

generando el mismo código (en clases parciales que crea él solo) que escribiríamos si elegimos la segunda opción. En

general os recomiendo que uséis el diseñador de LINQ to SQL para configurar los SPROCs en el 90% de los casos - y

crear las llamadas personalizadas a procedimientos almacenados en escenarios más avanzados.

Paso 3: Hacer otras inserciones con un SPROC

Empezaremos cambiando nuestro modelo de datos para que use SPROCs con el objeto Order.

Primero nos vamos a la ventana de "Explorador de Servidores" (Server Explorer) de Visual Studio, expandimos el

nodo "Stored Procedures" de nuestra base de datos, hacemos clic con el botón derecho y elegimos la opción "Add New

Stored Procedure":

Creamos el nuevo procedimiento almacenado que llamaremos "InsertOrder" que añade una nueva fila order a la tabla

Orders:

Page 8: Linq to sql 7

Fijáos que hemos definido el parámetro "OrderId" como un parámetro de salida. ESto es debido a que la columna

OrderID es una columna identidad que se autoincrementa cada vez que se añade un nuevo registro. Quien llame a este

SPROC deverá pasarle un valor null en ese parámetro - y el SPROC devolverá en ese parámetro el nuevo valor

OrderID (llamando a la función SCOPE_IDENTITY() al final del SPROC).

Después de crear el SPROC abrimos el diseñador de LINQ to SQL. De la misma forma que vimos en la sexta parte de

esta serie, podemos arrastrar y soltar SPROCs desde la ventana "server explorer" al diseñador. Esto es lo que haremos

con el nuevo SPROC que acabamos de crear:

Page 9: Linq to sql 7

El último paso será decirle a nuestra capa de datos que use el SPROC InsertOrder cuano inserter un nuevo objeto Order

en la base de datos. Esto lo hacemos seleccionando la clase "Order" del diseñador LINQ to SQL, y en las propiedades

clicamos el botón "..." del método Insert:

Hacemos clic en el botón "..." y aparecerá una ventana que nos permite personalizar las operaciones de inserción:

Page 10: Linq to sql 7

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.

Page 11: Linq to sql 7

Una vez que cliquemos en OK está listo. Ahora cada vez que añadamos un nuevo pedido a nuestro DataContext e

invoquemos al método SubmitChanges(), se ejecutará el SPROC InsertOrder.

Importante: Aunque estemos usando SPROC para la persistencia, el método parcial "OnValidate()" que creamos (en la

primer parte de esta serie) para encapsular las reglas de validación para los pedidos seguirán ejecutándose antes de

realizar cualquier cambio. Es decir, tenemos una forma limpia de encapsular la lógica de negocio y las reglas de

validación en nuestros modelos de datos, y podemos reutilizarlos tanto si usamos SQL o SPROCS.

Paso 4: Actualizando los clientes con SPROCs.

Ahora vamos a modificar el objeto Customer para manejar las actualizaciones con un SPROC.

Empezamos creando el SPROC "UpdateCustomer":

Fijaos que además de pasar el parámetro @CustomerID, también tenemos un parámetro @Original_CustomerID. La

columna CustomerID de la tabla Customers no es un campo autoincremental, y puede modificarse cuando hagamos

una actualización. Por tanto necesitamos ser capaces de decirle al SPROC cual es el CustomerID original y el nuevo

CustomerID. Vamos a ver cómo mapeamos esto con el diseñador de LINQ to SQL.

Veréis que estamos pasando un parámetro llamado @Version (que es una marca de tiempo) al SPROC. Es una nueva

columna que he añadido a la tabla Customers para ayudarme a controlar la concurrencia optimista. Veremos en más

detalle este tema en otro post de esta serie - pero en resumen es que LINQ to SQL soporta completamente la

concurrencia optimista, y nos permite usar tanto una marca de tiempo o usar valores original/nuevo para detectar si ha

habido algún cambio por parte de otro usuario ántes de guardar los datos. Para este ejemplo usaremos una marca de

tiempo ya que hace que el código sea mucho más claro.

Una vez que tenemos nuestro SPROC, lo arrastramos y soltamos al diseñador LINQ to SQL para añadirlo como

método a nuestro DataContext. Seleccionamos la clase Customer y hacemos clic en el botón "..." de la propiedad

Update:

Page 12: Linq to sql 7

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.

Page 13: Linq to sql 7

Cuando hacemos clic en OK ya esta terminado. Ahora cuando actualizemos cualquier cliente y llamemos a

SubmitChanges() se ejectuará el SPROC UpdateCustomer en lugar de ejecutarse un SQL dinámico.

Importante: Aunque ahora estemos usando SPROC, el método parcial "OnPhoneChanging()" de la clase Customer

(que creamos en el primer post de esta serie) para validar los números de teléfono se seguirá ejecutando de la misma

manera ántes de que se guarden los cambios. Tenemos de esta forma una forma limpia de encapsular reglas de negocio

y validación a nuestros modelos de datos, y podemos reutilizarlos tanto si usamos SQL dinámico o SPROCs.

Paso 5: Usando el modelo de datos otra vez (esta vez con SPROCs)

Ahora que ya tenemos configurada nuestra capa de datos para usar SPOCs en lugar de SQL dinámico, podemos

ejecutar el mismo código que vimos en el paso 2:

Ahora las actualizacion del objeto Customer, y la inserción del objeto ORder, se están ejecutando a través de SPROCs

en lugar de SQL dinámico. La lógica de validación que definimos se siguen ejecutando como antes, y el código sigue

siendo exactamente el mismo.

Apuntes avanzados cuando usamos SPROCs

Veamos unas cuantas recomendaciones útiles para escenarios con SPROC más avanzados con LINQ to SQL

Uso de parámetros de salida

En casos de inserción (Paso 3) hemos visto cómo podemos devolver el nuevo valor OrderID (que es un valor

identidad y autoincremental de la tabla Orders) usando un parámetro de salida en el SPROC. No estamos limitados a

devolver sólo valores de columnas identidad con SPROCs y LINQ to SQL - en realidad podemos actualizar y devolver

cualquier parámetro. Podemos usarlo tanto para insetar como para actualizar. LINQ to SQL tomará el valor resultado y

actualizará la propiedad asociada en el modelo de dato sin que tengamos que hacer ninguna consulta extra para

refrescarlo o calcularlo de nuevo.

¿Que pasa si el SPROC da un error?

Si el SPROC da un error mientras inserta, actualiza o borra un dato, LINQ to SQL cancelará y deshará la transacción

de todos los cambios asociados a la llamada SubmitChanges(). De manera que nos aseguramos la consistencia de los

datos.

¿Podemos escribir código en lugar de usar el diseñador para llamar a un SPROC?

Como ya comenté al principio, podemos usar tanto el diseñador de LINQ to SQL para mapear las operaciones con

SPROC o podemos añadir métodos parciales a la clase DataContext programáticamente e invocarlos nosotros mismo.

Page 14: Linq to sql 7

Aquí tenéis un ejemplo del código que deberíamos escribir para sobreescribir el método UpdateCustomer de la clase

NorthwindDataContext:

Este código es el que fué generado con el diseñador de LINQ to SQL cuando lo usamos para mapear el SPROC y

asociarlo a la operación de Update del objeto Customer. Podemos usarlo como un punto de partida y añadir alguna

lógica adicional para hacerlo más personalizado (por ejemplo: usar el valor de retorno del SPROC para lanzar

excepciones personalizadas).

Resumen

LINQ to SQL es un ORM muy flexible. Nos permite escribir código limpio orientado a objetos para obtener, acutalizar

e insertar datos.

Lo mejor de todo es que nos permite diseñar una capa de datos realmente limpia e independiente de cómo se guardan y

cargan los datos de la base de datos. Podemos usar SQL dinámico o SPROCs para esas operaciones. Lo mejor es que el

código que use nuestra capa de datos, y todas las reglas de negocio asociadas, serán las mismas sin importar que

método de persistencia estemos usando.

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.