Este documento resume una serie de artículos sobre LINQ to SQL, un ORM que permite modelar bases de datos relacionales en clases de .NET. Explica cómo LINQ to SQL genera consultas SQL dinámicas pero también permite ejecutar consultas SQL personalizadas a través del método ExecuteQuery. Este método permite especificar el tipo de datos devuelto y participa en el seguimiento de cambios para actualizaciones. También cubre cómo usar consultas SQL personalizadas para inserciones, actualizaciones y eliminaciones.
1. LINQ to SQL (Parte 8 – Ejecutar consultas SQL personalizadas)
4 respuestas
En las últimas semanas he escrito una serie de post sobre LINQ to SQL. LINQ to SQL es un ORM que viene con .NET 3.5, y nos permite modelar bases de datos relacionales en clases. Podemos usar expresiones LINQ para consultar la base de datos y también para actualizar, insertar y borrar datos.
Aquí teneis los enlaces a los diferentes post de la serie:
Parte 1: Introducción a LINQ to SQL
Parte 2: Definiendo el modelo de datos.
Parte 3: Consultando la base de datos
Parte 4: Actualizando la base de datos.
Parte 5: Enlazar controles de interfaz de usuario con el ASP:LinqDatSource
Parte 6: Obtener datos con procedimientos almacenados.
Parte 7: Actualizando la base de datos con procedimientos almacenados.
En los dos últimos post vismo cómo podemos usar los procedimientos almacenados de nuestra base de datos para consultar, insertar, actualizar y borrar datos con el modelo de LINQ to SQL.
Una pregunta que me han hecho mucho desde que he escrito estos post es: ¿que pasa si quiero control total sobre las consultas SQL que usa LINQ to SQL - pero no quiero usar SPROCs para hacerlo? En el post de hoy veremos eso - y veremos cómo podemos usar expresiones SQL personalizadas para que LINQ to SQL las use en lugar de las que generaría él.
Uso de expresiones LINQ con LINQ to SQL.
Supongamos que hemos usado el diseñador de LINQ to SQL de VS 2008 para modelar un conjunto de clases a partir de la base de datos Northwind (esto lo vimos en el segundo post de la serie):
2. En el tercer post vimos cómo podemos usar LINQ con las nuevas características de VB y C# para consultar el modelo de clases y devolver un conjunto de objetos que representan las filas y columnas de la base de datos.
Por ejemplo, podemos añadir un método a la clase DataContext "GetProductsByCategory" que usa una consulta LINQ para devolver objetos de Products de la base de datos:
VB:
c#:
3. 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.
4. Lo que hace que ExecuteQuery sea tan útil es que nos permite especifiar cómo queremos devolver los valores de la expresión SQL. Podemos hacer esto pasándole un parámetro tipado al método o usando una versión genérica del método.
Por ejemplo, podemos cambiar el método GetProductsByCategory() que creamos ántes -con una expresión LINQ- para que use el método ExecuteQuery para ejectuar un SQL que nosotros le digamos:
VB:
C#:
Ahora podemos llamar al método GetProductsByCategory() de la misma forma que ántes:
De esta manera será nuestra consulta SQL la que se ejecutará contra la base de datos - y no el SQL dinámico que generaría la expresión LINQ.
SQL personalizado y tracking de objetos para las actualizaciones
Por defecto cuando obtenemos objetos con LINQ to SQL, se hace un tracking sobre los cambios que les hacemos. Si llamamos al método "SubmitChanges()" guardará los datos de forma transaccional en la base de datos. Vismo esto en la cuarta parte de esta serie de post.
Una de las característcias del metodo ExecuteQuery() es que participa en este tracking de objetos para actualizar el modelo. Por ejemplo, podemos escribir el siguiente código para obtener todos los productos de una categoría y rebajar los precios un 10%:
5. 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 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.
6. Esto lo conseguimos creando los métodos parciales adecuados para cada operacion para la entidad que queramos cambiar en nuestra clase DataContext. Podemos usar el método ExecuteCommand del DataContext para escribir el SQL que queramos. Por ejemplo, para sobreescribir el comportamiento de borrado de la clase Product definimos el siguiente método parcial:
Y si escribimos un código que elimine un producto de la base de datos, LINQ to SQL llamará al método DeleteProduct - que ejecutará una SQL personalizada en lugar del SQL dinámico que LINQ to SQL usaría:
Resumen
El ORM LINQ to SQL genera y ejectua un SQL dinámico para las consultas, actualizaciones, inserciones y borrados contra la base de datos.
Para escenarios más avanzados, o en caso donde queramos un control total sobre el SQL que se ejecuta, también podemos personalizar el ORM para que ejecute SPROCs, o nuestras consultas SQL personalizadas. Esto nos da una gran flexibilidad a la hora de construir y extender nuestra capa de datos.
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.