Continuando con nuestra saga .NET Framework, vamos a hablar un poco acerca de lo que es un ORM y en particular del ORM de Microsoft: Entity Framework. La traducción literal de Entity Framework al español sería algo así como "Marco de Trabajo de Entidades". Sin embargo, en el contexto más técnico, se prefiere mantener el nombre en inglés porque es un término propio y ampliamente reconocido en el mundo del desarrollo de software. Ahora sí vamos con la definición.
¿Qué es Entity Framework?
Bien, si partimos desde lo básico Entity Framework (EF) es un ORM (Object-Relational Mapper) que permite a los desarrolladores trabajar con bases de datos relacionales utilizando objetos de C# o VB en lugar de escribir código SQL directamente. Forma parte del ecosistema de .NET Framework y está diseñado para facilitar el acceso y manejo de datos hacia y desde la base de datos.
Sería como si el EF actuara como un "puente" entre la aplicación y la base de datos, generando automáticamente las consultas necesarias y convirtiendo los datos en objetos de C# que se pueden manipular fácilmente desde el código.
Fecha de lanzamiento: agosto de 2008 con .NET Framework 3.5 SP1. Versión que fue oficialmente conocida como EF 1.0 y formaba parte del entorno de Visual Studio 2008.
Características iniciales:
- Introdujo el modelo EDM (Entity Data Model), que permitía mapear clases de .NET a tablas de bases de datos.
- Soportaba consultas LINQ to Entities para interactuar con los datos de forma declarativa.
En el 2010, se incluyó en el .NET Framework 4.0 y VS 2010 la versión Entity Framework 4.0 donde se liberaron algunas mejoras en las criticas recibidas por la comunidad.
Novedades principales:
- Soporte para el enfoque Model First: Permitir diseñar el modelo y luego generar la base de datos.
- Mejora en el soporte para consultas LINQ y modelos complejos.
- Introducción del Lazy Loading.
Nota: No existió una versión EF 2.0 o 3.0 debido a la sincronización con el número de versión de .NET Framework.
En el 2011, se marca el lanzamientoe de Entity Framework 4.1, con el enfoque Code First donde los desarrolladores podían definir el modelo directamente en el código C# o VB sin depender de herramientas gráficas o XML. También incluyó el concepto de DbContext, simplificando la gestión de datos que ya hablaremos más delante.
Por último, cerrando una etapa se lanzó en el 2013 Entity Framework 6 junto con Visual Studio 2013, una de las versiones más estables y preferidas por la comunidad. Además de que se separó de .NET Framework para pasara a ser un proyecto de código abierto alojado en Github.
Características principales:
- Asincronía con soporte para async/await.
- Interceptores y comandos SQL personalizados.
- Mejoras en el rendimiento y en las migraciones de bases de datos.
De ahí en más, el desarrollo de EF pasó a evolucionar junto con .NET Core, pero esta entrada no va a abarcar aún esa información.
Ahora bien, todo esto queda un poco "suelto" si no sabemos qué es un ORM..
Definamos ORM
Un ORM es una herramienta informática que facilita el trabajo con bases de datos al permitir que los desarrolladores interactúen con los datos mediante objetos en lugar de usar directamente consultas SQL o queries. Esto reduce el esfuerzo en la manipulación de dichos datos y la escritura de código. Por supuesto no todo son pros, también tiene sus contrapartes que ya hablaremos de ellas.
Nota: Bases de Datos Relacionales, esto quiere decir que no se puede trabajar con bases de datos NO-SQL. No confundir con Entity Framework .NET Core.
Diferencias clave entre EF clásico y EF Core
Característica |
.NET Framework |
.NET Core (5/6/7) |
Soporte para NoSQL |
❌ No |
✅ Sí, con proveedores como Cosmos DB |
Arquitectura extensible |
❌ Limitada |
✅ Diseñada para extensibilidad |
Multiplataforma |
❌ Solo Windows |
✅ Windows, Linux, macOS |
Rendimiento |
Más lento |
Más rápido, gracias a optimizaciones |
Soporte a Cosmos DB |
❌ No |
✅ Proveedor oficial |
Otros lenguajes o sistemas también tienen su propio ORM, como Django para Pyton o Springboot para Java.
Una de las ventajas de utilizar EF, es que mejora la productividad y abstrae gran parte del trabajo repetitivo asociado con el acceso a datos, permitiendo que los desarrolladores se concentren en la lógica del negocio. Además de que tiene gran compatibilidad con LINQ, permitiendo usar consultas para interactuar con la base de datos, lo que resulta más intuitivo para algunos desarrolladores que escribir SQL puro.
Desventajas de Usar Entity Framework
Aunque Entity Framework (EF) es una herramienta poderosa y facilita el desarrollo, también tiene ciertas limitaciones y desafíos que los programadores deben tener en cuenta al trabajar con él.
- No soporta transacciones lógicas.
- Manejo de Estados y Cambios en las Entidades.
Entity Framework (EF) utiliza un modelo de seguimiento de estados (Change Tracking) para identificar y aplicar cambios en las entidades, que son las clases en el código que representan tablas en la base de datos. Este modelo permite que EF reconozca si una entidad necesita ser insertada, actualizada o eliminada en la base de datos. Sin embargo, este sistema puede complicar algunos escenarios, especialmente cuando una entidad cargada previamente se utiliza en un contexto diferente, lo que puede generar excepciones de estado, como InvalidOperationException.
En EF, una misma entidad puede tener múltiples instancias en diferentes estados, lo que puede dar lugar a problemas comunes. Por ejemplo, un objeto puede quedar en estado Detached (desacoplado), lo que significa que EF no lo está rastreando. Si intentas guardar una entidad en este estado sin volver a adjuntarla al contexto, se generarán errores.
Otro aspecto importante es que, aunque EF soporta validaciones básicas a través de atributos como [Required], [StringLength], y otros, estas validaciones son insuficientes para cubrir reglas de negocio más complejas. Por esta razón, es común utilizar una clase "validadora" personalizada para ejecutar validaciones adicionales de forma manual, permitiendo un control más detallado sobre la lógica de negocio.
Modelos de desarrollo
A nivel de diseño, Entity Framework nos ofrece tres enfoques principales para modelar datos, cada uno adecuado para diferentes escenarios:
Code First: Este enfoque permite definir el modelo de datos directamente en el código, utilizando clases y atributos de C#. Posteriormente, EF genera la base de datos a partir de estas definiciones.
Ventajas: Ideal para nuevos proyectos donde aún no existe una base de datos.
Control total sobre el diseño del modelo y es compatible con migraciones para manejar cambios en la DB.
Con Code First, Entity Framework traduce cada clase en una tabla de base de datos, asignando el nombre de la clase a la tabla. Las propiedades públicas de la clase se mapean a las columnas de la tabla, mientras que los métodos y constructores son ignorados.
Para configurar las entidades, se utilizan anotaciones, como:
- [Key]: Define la clave primaria.
- [DatabaseGenerated(Identity)]: Indica que el campo es una identidad numérica autoincremental.
- [Required]: Marca una propiedad como obligatoria.
- [MaxLength(xx)] y [MinLength(xx)]: Establecen restricciones de longitud.
- [NotMapped]: Excluye una propiedad del mapeo a la base de datos.
Entre otras.
Database First: Con este modelo, se parte de una base de datos existente, y EF genera las clases necesarias para interactuar con ella.
Ventajas: Perfecto para proyectos donde la base de datos ya está diseñada.
Menor tiempo de configuración inicial y debo reconocer que funciona de manera sorprendentemente eficiente (tener presente que lo digo yo, que soy lo más reacio a la automatización de código).
Model First: Aquí se utiliza un diseñador para modelar las tablas y relaciones, generando luego tanto el código como la base de datos a partir del modelo visual. Son herramientas visuales como EDMX Designer, el creador de diagramas de SQL SSMS o Microsoft Visio por ejemplo.
Ventajas: Útil para quienes prefieren trabajar con estas herramientas. Entre las etapas automáticas de esta opción, se encuentra la de generar las entities y crear la base de datos desde el modelo.
Características
EF automatiza las operaciones básicas sobre las entidades, pero también permite usar procedimientos almacenados (SP, Stored Procedure) personalizados. Si bien se pueden invocar SP desde EF, un problema común es que no reconoce los valores de retorno. Para solucionarlo, se debe cargar un parámetro de tipo output para procesar correctamente estos valores.
En ocasiones, es necesario recurrir a ADO .NET de manera manual, como "puerta trasera" de EF, para manejar resultados específicos (por ejemplo, valores de retorno). Sin embargo, en estos casos, EF no detecta los cambios realizados directamente en la base de datos, por lo que se requiere actualizar manualmente el contexto de EF.
Cómo gestiona las consultas en la base de datos
EF ofrece diferentes estrategias de carga de datos:
- Eager Loading: Trae todos los datos relacionados cuando se carga un objeto contenedor (por ejemplo, una factura con sus líneas de artículos). Esto es útil cuando se necesita acceder a toda la información.
- Lazy Loading: Solo carga el objeto contenedor y sus dependencias se cargan bajo demanda. Es más eficiente en cuanto a uso de memoria, ya que no trae datos adicionales hasta que se solicitan explícitamente.
- Explicit Loading: Permite cargar explícitamente solo lo que se necesita, como ciertos campos de un objeto, sin traer dependencias completas.
Se utilizan varias clases esenciales para interactuar con la base de datos. Algunas de las principales son:
- Database: Representa la base de datos relacional desde donde se consultan y hacia donde se persisten las operaciones de ABM (Alta, Baja, Modificación). En algunos casos, esta clase se utiliza para impactar la base de datos o crearla.
- DbContext: Es la clase que gestiona el comportamiento de Entity Framework. Contiene el modelo de trabajo y se encarga de la interacción con la base de datos. A través de esta clase, se manejan los DbSet, que representan las tablas de la base de datos y las operaciones de acceso a datos.
Algunas de las funcionalidades del DBContext y que son imprescindibles conocerlas, son las siguientes:
- SaveChanges(): Esta operación afecta directamente a la base de datos. Revisa todos los DbSet y dependiendo del estado de las entidades, realiza diferentes acciones:
- Added: Si una entidad está marcada como añadida, se genera un INSERT en la base de datos. Después de ejecutar correctamente la operación, el estado de la entidad se cambia a Unchanged.
Ejemplo:
using (var context = new MyDbContext())
{
// Crear una nueva instancia de la entidad
var nuevoProducto = new Producto
{
Nombre = "Nuevo Producto",
Precio = 100.0
};
// Agregar el producto al DbSet
context.Productos.Add(nuevoProducto);
// Guardar los cambios en la base de datos
context.SaveChanges();
// El estado del objeto cambia a Unchanged
Console.WriteLine("Estado después de guardar: " + context.Entry(nuevoProducto).State);
}
Deleted: Si una entidad está marcada como eliminada, se genera un DELETE. Si la operación es exitosa, el objeto se elimina del contexto.
Ejemplo:
using (var context = new MyDbContext())
{
// Buscar un producto existente
var productoExistente = context.Productos.FirstOrDefault(p => p.Nombre == "Producto a eliminar");
if (productoExistente != null)
{
// Marcar el producto para eliminación
context.Productos.Remove(productoExistente);
// Guardar los cambios en la base de datos
context.SaveChanges();
// El objeto es removido del contexto
Console.WriteLine("Estado después de eliminar: " + context.Entry(productoExistente).State);
}
else
{
Console.WriteLine("Producto no encontrado.");
}
}
Modified: Si una entidad está modificada, se genera un UPDATE. Una vez realizado, el estado también se cambia a Unchanged.
Ejemplo:
using (var context = new MyDbContext())
{
// Buscar un producto existente
var productoExistente = context.Productos.FirstOrDefault(p => p.Nombre == "Producto a modificar");
if (productoExistente != null)
{
// Modificar una propiedad de la entidad
productoExistente.Precio = 120.0;
// Guardar los cambios en la base de datos
context.SaveChanges();
// El estado del objeto cambia a Unchanged después de guardar
Console.WriteLine("Estado después de modificar: " + context.Entry(productoExistente).State);
}
else
{
Console.WriteLine("Producto no encontrado.");
}
}
El estado Unchanged indica que el objeto es una representación fiel de la base de datos, es decir, no ha sufrido cambios pendientes.
- Entry.State: La operación Entry se usa para obtener el estado de una entidad en el DbContext. Si una entidad está en el estado Detached, significa que el objeto ha sido eliminado del contexto, y no tiene más seguimiento. Si se encuentra el objeto, se puede modificar su estado, lo que es útil si se quiere sacar un objeto de un DbSet.
Por ejemplo, se puede cambiar manualmente el estado de una Entity a detached:
contexto.Entry(articulo).State = System.Data.Entity.EntityState.Detached;
- Database.ExecuteSqlCommand: Esta función permite ejecutar procedimientos almacenados (SP) con parámetros a través del contexto de la base de datos. Sin embargo, el contexto no está al tanto de lo que hace el SP, por lo que se debe forzar la carga de datos después de ejecutar el SP.
En resumen, los puntos que hemos tocado hoy son fundamentales para obtener un conocimiento introductorio sobre Entity Framework. Desde entender el modelo de seguimiento de estados hasta la definición de clases esenciales como DbContext y Database. No es menor mencionar que, si algún día aspirás a trabajar en grandes empresas tecnológicas, como Microsoft (porque soñar en grande es válido), contar con un manejo sólido de herramientas como Entity Framework puede marcar la diferencia.
No solo te va a ayudar a desenvolverte mejor en tu día a día, sino también a responder con seguridad ante cualquier pregunta técnica que pueda surgir en una entrevista.
En la próxima entrada, vamos a explorar cómo crear un proyecto desde cero utilizando el enfoque Database First, acompañado de ejemplos prácticos de consultas LINQ para manejar datos de forma eficiente. Hasta entonces 👍
Nota especial: Mis agradecimientos al proyecto de Code Beautify que de forma gratuita nos permite generar fácilmente el formato de código y te lo escribe como html para tu página. Muy bien logrado, con varios templates de diseño para elegir.