Entity Framework es una gran herramienta (un ORM) que nos permite trabajar con bases de datos utilizando un enfoque orientado a objetos.
Sin embargo, en ocasiones aún necesitamos ejecutar consultas SQL directas (no muy a menudo, pero a veces lo necesitaremos).
Esto nos permite exprimir al máximo las capacidades de la base de datos, por ejemplo para realizar operaciones complejas que no son fáciles de expresar con LINQ.
¿Por qué usar consultas SQL directas?
- Consultas muy complejas: Cuando una consulta es demasiado compleja para expresarla con LINQ.
- Optimización de rendimiento: Aprovechar características específicas de la base de datos.
- Operaciones específicas: Cuando necesitamos ejecutar comandos SQL que no tienen un equivalente directo en LINQ.
Entity Framework proporciona dos métodos principales para ejecutar consultas SQL directas: FromSqlRaw
y ExecuteSqlRaw
. Vamos a ver cómo utilizarlos 👇
Consultas SQL que devuelven datos FromSqlRaw
El método FromSqlRaw
se utiliza para ejecutar consultas SQL que devuelven datos. Estos datos se mapean automáticamente a las entidades de nuestro modelo.
Veamos un ejemplo básico:
var productos = context.Productos
.FromSqlRaw("SELECT * FROM Productos WHERE Precio > {0}", 50)
.ToList();
En este ejemplo,
- Estamos ejecutando una consulta SQL que selecciona todos los productos con un precio mayor a 50.
- El resultado se mapea automáticamente a la entidad
Producto
.
Comandos SQL que no devuelven datos con ExecuteSqlRaw
Por su parte, el método ExecuteSqlRaw
se utiliza para ejecutar comandos SQL que no devuelven datos (como INSERT
, UPDATE
, DELETE
o TRUNCATE TABLE
).
Este método devuelve el número de filas afectadas por el comando.
var filasAfectadas = context.Database.ExecuteSqlRaw("UPDATE Productos SET Precio = Precio * 1.1 WHERE CategoriaId = {0}", 1);
Console.WriteLine($"Filas afectadas: {filasAfectadas}");
En este ejemplo, estamos actualizando el precio de todos los productos en la categoría con CategoriaId = 1
, incrementándolo en un 10%.
Consideraciones de Seguridad
Es importante evitar la concatenación de cadenas para incluir parámetros en las consultas SQL, ya que esto puede llevar a vulnerabilidades de inyección SQL.
En su lugar, debemos utilizar parámetros con marcadores de posición ({0}
, {1}
, etc.) o parámetros con nombre. Por ejemplo,
var precioMinimo = 50;
var productos = context.Productos
.FromSqlRaw("SELECT * FROM Productos WHERE Precio > {0}", precioMinimo)
.ToList();
También podemos utilizar parámetros con nombre:
var precioMinimo = 50;
var productos = context.Productos
.FromSqlRaw("SELECT * FROM Productos WHERE Precio > @precioMinimo", new SqlParameter("@precioMinimo", precioMinimo))
.ToList();
- Usar parámetros: Siempre utiliza parámetros en lugar de concatenar cadenas.
- Validar entradas: Asegúrate de validar y sanitizar cualquier entrada del usuario antes de utilizarla en una consulta SQL.
Ejemplos prácticos
Consulta SQL con FromSqlRaw
Supongamos que tenemos una base de datos de productos y queremos obtener todos los productos de una categoría específica que tengan un precio superior a un valor dado.
var categoriaId = 1;
var precioMinimo = 100;
var productos = context.Productos
.FromSqlRaw("SELECT * FROM Productos WHERE CategoriaId = {0} AND Precio > {1}", categoriaId, precioMinimo)
.ToList();
Combinación con LINQ
Una de las ventajas de FromSqlRaw
es que podemos combinar consultas SQL con LINQ para realizar operaciones adicionales, como filtrado, ordenamiento o proyección.
var productos = context.Productos
.FromSqlRaw("SELECT * FROM Productos WHERE Precio > {0}", 50)
.OrderBy(p => p.Nombre)
.ToList();
En este ejemplo, estamos seleccionando productos con un precio mayor a 50 y luego ordenándolos por nombre utilizando LINQ.
Actualización masiva con ExecuteSqlRaw
Imaginemos que queremos aplicar un descuento del 20% a todos los productos de una categoría específica.
var categoriaId = 2;
var filasAfectadas = context.Database.ExecuteSqlRaw("UPDATE Productos SET Precio = Precio * 0.8 WHERE CategoriaId = {0}", categoriaId);
Console.WriteLine($"Filas afectadas: {filasAfectadas}");
Eliminación de registros con ExecuteSqlRaw
Si necesitamos eliminar todos los productos que no tienen stock, podemos hacerlo de la siguiente manera:
var filasAfectadas = context.Database.ExecuteSqlRaw("DELETE FROM Productos WHERE Stock = 0");
Console.WriteLine($"Filas afectadas: {filasAfectadas}");